]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - virtual/eldiron.nix
Move websites to their own modules: php config and scripts
[perso/Immae/Config/Nix.git] / virtual / eldiron.nix
1 {
2 network = {
3 description = "Immae's network";
4 enableRollback = true;
5 };
6
7 eldiron = { config, pkgs, mylibs, ... }:
8 with mylibs;
9 let
10 mypkgs = pkgs.callPackage ./packages.nix {
11 inherit checkEnv fetchedGit fetchedGitPrivate fetchedGithub;
12 };
13 in
14 {
15 _module.args = {
16 mylibs = import ../libs.nix;
17 };
18
19 imports = [
20 ./modules/certificates.nix
21 ./modules/gitolite.nix
22 ./modules/gitweb.nix
23 ./modules/databases.nix
24 ./modules/websites/chloe.nix
25 ./modules/websites/ludivine.nix
26 ./modules/websites/aten.nix
27 ./modules/websites/piedsjaloux.nix
28 ./modules/websites/connexionswing.nix
29 ];
30 services.myGitolite.enable = true;
31 services.myGitweb.enable = true;
32 services.myDatabases.enable = true;
33 services.myWebsites.Chloe.production.enable = true;
34 services.myWebsites.Chloe.integration.enable = true;
35 services.myWebsites.Ludivine.production.enable = true;
36 services.myWebsites.Ludivine.integration.enable = true;
37 services.myWebsites.Aten.production.enable = true;
38 services.myWebsites.Aten.integration.enable = true;
39 services.myWebsites.PiedsJaloux.production.enable = true;
40 services.myWebsites.PiedsJaloux.integration.enable = true;
41 services.myWebsites.Connexionswing.production.enable = true;
42 services.myWebsites.Connexionswing.integration.enable = true;
43
44 nixpkgs.config.packageOverrides = oldpkgs: rec {
45 goaccess = oldpkgs.goaccess.overrideAttrs(old: rec {
46 name = "goaccess-${version}";
47 version = "1.3";
48 src = pkgs.fetchurl {
49 url = "https://tar.goaccess.io/${name}.tar.gz";
50 sha256 = "16vv3pj7pbraq173wlxa89jjsd279004j4kgzlrsk1dz4if5qxwc";
51 };
52 configureFlags = old.configureFlags ++ [ "--enable-tcb=btree" ];
53 buildInputs = old.buildInputs ++ [ pkgs.tokyocabinet pkgs.bzip2 ];
54 });
55 };
56
57 networking = {
58 firewall = {
59 enable = true;
60 allowedTCPPorts = [ 22 80 443 9418 ];
61 };
62 };
63
64 deployment = {
65 targetEnv = "hetzner";
66 hetzner = {
67 #robotUser = "defined in HETZNER_ROBOT_USER";
68 #robotPass = "defined in HETZNER_ROBOT_PASS";
69 mainIPv4 = "176.9.151.89";
70 partitions = ''
71 clearpart --all --initlabel --drives=sda,sdb
72
73 part swap1 --recommended --label=swap1 --fstype=swap --ondisk=sda
74 part swap2 --recommended --label=swap2 --fstype=swap --ondisk=sdb
75
76 part raid.1 --grow --ondisk=sda
77 part raid.2 --grow --ondisk=sdb
78
79 raid / --level=1 --device=md0 --fstype=ext4 --label=root raid.1 raid.2
80 '';
81 };
82 };
83
84 environment.systemPackages = let
85 # FIXME: move it to nextcloud
86 occ = pkgs.writeScriptBin "nextcloud-occ" ''
87 #! ${pkgs.stdenv.shell}
88 cd ${mypkgs.nextcloud.webRoot}
89 NEXTCLOUD_CONFIG_DIR="${mypkgs.nextcloud.webRoot}/config" \
90 exec \
91 ${config.services.phpfpm.phpPackage}/bin/php \
92 -c ${config.services.phpfpm.phpPackage}/etc/php.ini \
93 occ $*
94 '';
95 in [
96 pkgs.telnet
97 pkgs.htop
98 pkgs.vim
99 pkgs.goaccess
100 occ
101 ];
102
103 security.acme.certs."eldiron".extraDomains = {
104 "db-1.immae.eu" = null;
105 "tools.immae.eu" = null;
106 "cloud.immae.eu" = null;
107 "dav.immae.eu" = null;
108 };
109
110 services.openssh.extraConfig = ''
111 AuthorizedKeysCommand /etc/ssh/ldap_authorized_keys
112 AuthorizedKeysCommandUser nobody
113 '';
114
115 services.ympd = mypkgs.ympd.config // { enable = false; };
116
117 services.phpfpm = {
118 # FIXME: move session files to separate dirs
119 # /!\ phppackage is used in nextcloud configuation
120 phpOptions = ''
121 session.save_path = "/var/lib/php/sessions"
122 session.gc_maxlifetime = 60*60*24*15
123 session.cache_expire = 60*24*30
124 ; For nextcloud
125 extension=${pkgs.phpPackages.redis}/lib/php/extensions/redis.so
126 ; For nextcloud
127 extension=${pkgs.phpPackages.apcu}/lib/php/extensions/apcu.so
128 ; For nextcloud
129 zend_extension=${pkgs.php}/lib/php/extensions/opcache.so
130 '';
131 extraConfig = ''
132 log_level = notice
133 '';
134 poolConfigs = {
135 adminer = mypkgs.adminer.phpFpm.pool;
136 nextcloud = mypkgs.nextcloud.phpFpm.pool;
137 mantisbt = mypkgs.mantisbt.phpFpm.pool;
138 ttrss = mypkgs.ttrss.phpFpm.pool;
139 roundcubemail = mypkgs.roundcubemail.phpFpm.pool;
140 davical = mypkgs.davical.phpFpm.pool;
141 };
142 };
143
144 system.activationScripts = {
145 nextcloud = mypkgs.nextcloud.activationScript;
146 ttrss = mypkgs.ttrss.activationScript;
147 roundcubemail = mypkgs.roundcubemail.activationScript;
148 httpd = ''
149 install -d -m 0755 /var/lib/acme/acme-challenge
150 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions
151 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/adminer
152 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/mantisbt
153 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/ttrss
154 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/davical
155 '';
156 redis = ''
157 mkdir -p /run/redis
158 chown redis /run/redis
159 '';
160 # FIXME: initial sync
161 goaccess = ''
162 mkdir -p /var/lib/goaccess
163 mkdir -p /var/lib/goaccess/aten.pro
164 mkdir -p /var/lib/goaccess/ludivinecassal.com
165 mkdir -p /var/lib/goaccess/piedsjaloux.fr
166 mkdir -p /var/lib/goaccess/osteopathe-cc.fr
167 mkdir -p /var/lib/goaccess/connexionswing.com
168 '';
169 };
170
171 environment.etc."ssh/ldap_authorized_keys" = let
172 ldap_authorized_keys =
173 assert checkEnv "NIXOPS_SSHD_LDAP_PASSWORD";
174 wrap {
175 name = "ldap_authorized_keys";
176 file = ./ldap_authorized_keys.sh;
177 vars = {
178 LDAP_PASS = builtins.getEnv "NIXOPS_SSHD_LDAP_PASSWORD";
179 GITOLITE_SHELL = "${pkgs.gitolite}/bin/gitolite-shell";
180 ECHO = "${pkgs.coreutils}/bin/echo";
181 };
182 paths = [ pkgs.openldap pkgs.stdenv.shellPackage pkgs.gnugrep pkgs.gnused pkgs.coreutils ];
183 };
184 in {
185 enable = true;
186 mode = "0755";
187 user = "root";
188 source = ldap_authorized_keys;
189 };
190
191 services.gitDaemon = {
192 enable = true;
193 user = "gitolite";
194 group = "gitolite";
195 basePath = "${mypkgs.git.web.varDir}/repositories";
196 };
197
198 # FIXME: logrotate
199 services.httpd = let
200 withConf = domain: {
201 enableSSL = true;
202 sslServerCert = "/var/lib/acme/${domain}/cert.pem";
203 sslServerKey = "/var/lib/acme/${domain}/key.pem";
204 sslServerChain = "/var/lib/acme/${domain}/fullchain.pem";
205 logFormat = "combinedVhost";
206 listen = [ { ip = "*"; port = 443; } ];
207 };
208 apacheConfig = {
209 gzip = {
210 modules = [ "deflate" "filter" ];
211 extraConfig = ''
212 AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
213 '';
214 };
215 ldap = {
216 modules = [ "ldap" "authnz_ldap" ];
217 extraConfig = assert checkEnv "NIXOPS_HTTP_LDAP_PASSWORD"; ''
218 <IfModule ldap_module>
219 LDAPSharedCacheSize 500000
220 LDAPCacheEntries 1024
221 LDAPCacheTTL 600
222 LDAPOpCacheEntries 1024
223 LDAPOpCacheTTL 600
224 </IfModule>
225
226 <Macro LDAPConnect>
227 <IfModule authnz_ldap_module>
228 AuthLDAPURL ldap://ldap.immae.eu:389/dc=immae,dc=eu
229 AuthLDAPBindDN cn=httpd,ou=services,dc=immae,dc=eu
230 AuthLDAPBindPassword "${builtins.getEnv "NIXOPS_HTTP_LDAP_PASSWORD"}"
231 AuthType Basic
232 AuthName "Authentification requise (Acces LDAP)"
233 AuthBasicProvider ldap
234 </IfModule>
235 </Macro>
236
237 <Macro Stats %{domain}>
238 Alias /awstats /var/lib/goaccess/%{domain}
239 <Directory /var/lib/goaccess/%{domain}>
240 DirectoryIndex index.html
241 AllowOverride None
242 Require all granted
243 </Directory>
244 <Location /awstats>
245 Use LDAPConnect
246 Require ldap-group cn=%{domain},ou=stats,cn=httpd,ou=services,dc=immae,dc=eu
247 </Location>
248 </Macro>
249 '';
250 };
251 http2 = {
252 modules = [ "http2" ];
253 extraConfig = ''
254 Protocols h2 http/1.1
255 '';
256 };
257 customLog = {
258 modules = [];
259 extraConfig = ''
260 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedVhost
261 '';
262 };
263 };
264 in rec {
265 enable = true;
266 logPerVirtualHost = true;
267 multiProcessingModule = "worker";
268 adminAddr = "httpd@immae.eu";
269 logFormat = "combinedVhost";
270 extraModules = pkgs.lib.lists.unique (
271 mypkgs.adminer.apache.modules ++
272 mypkgs.nextcloud.apache.modules ++
273 mypkgs.connexionswing_dev.apache.modules ++
274 mypkgs.connexionswing_prod.apache.modules ++
275 mypkgs.ludivinecassal_dev.apache.modules ++
276 mypkgs.ludivinecassal_prod.apache.modules ++
277 mypkgs.piedsjaloux_dev.apache.modules ++
278 mypkgs.piedsjaloux_prod.apache.modules ++
279 mypkgs.chloe_dev.apache.modules ++
280 mypkgs.chloe_prod.apache.modules ++
281 mypkgs.aten_dev.apache.modules ++
282 mypkgs.aten_prod.apache.modules ++
283 mypkgs.ympd.apache.modules ++
284 mypkgs.git.web.apache.modules ++
285 mypkgs.mantisbt.apache.modules ++
286 mypkgs.ttrss.apache.modules ++
287 mypkgs.roundcubemail.apache.modules ++
288 pkgs.lib.lists.flatten (pkgs.lib.attrsets.mapAttrsToList (n: v: v.modules) apacheConfig) ++
289 [ "macro" ]);
290 extraConfig = builtins.concatStringsSep "\n"
291 (pkgs.lib.attrsets.mapAttrsToList (n: v: v.extraConfig) apacheConfig);
292 virtualHosts = [
293 (withConf "eldiron" // {
294 hostName = "eldiron.immae.eu";
295 documentRoot = ./www;
296 extraConfig = ''
297 DirectoryIndex index.htm
298 '';
299 })
300 (withConf "eldiron" // {
301 hostName = "db-1.immae.eu";
302 documentRoot = null;
303 extraConfig = builtins.concatStringsSep "\n" [
304 mypkgs.adminer.apache.vhostConf
305 ];
306 })
307 (withConf "eldiron" // {
308 hostName = "tools.immae.eu";
309 documentRoot = null;
310 extraConfig = builtins.concatStringsSep "\n" [
311 mypkgs.adminer.apache.vhostConf
312 mypkgs.ympd.apache.vhostConf
313 mypkgs.ttrss.apache.vhostConf
314 mypkgs.roundcubemail.apache.vhostConf
315 ];
316 })
317 (withConf "eldiron" // {
318 hostName = "dav.immae.eu";
319 documentRoot = null;
320 extraConfig = builtins.concatStringsSep "\n" [
321 mypkgs.infcloud.apache.vhostConf
322 mypkgs.davical.apache.vhostConf
323 ];
324 })
325 (withConf "eldiron" // {
326 hostName = "connexionswing.immae.eu";
327 serverAliases = [ "sandetludo.immae.eu" ];
328 documentRoot = mypkgs.connexionswing_dev.webRoot;
329 extraConfig = builtins.concatStringsSep "\n" [
330 mypkgs.connexionswing_dev.apache.vhostConf
331 ];
332 })
333 (withConf "connexionswing" // {
334 hostName = "connexionswing.com";
335 serverAliases = [ "sandetludo.com" "www.connexionswing.com" "www.sandetludo.com" ];
336 documentRoot = mypkgs.connexionswing_prod.webRoot;
337 extraConfig = builtins.concatStringsSep "\n" [
338 mypkgs.connexionswing_prod.apache.vhostConf
339 ];
340 })
341 (withConf "eldiron" // {
342 hostName = "ludivine.immae.eu";
343 documentRoot = mypkgs.ludivinecassal_dev.webRoot;
344 extraConfig = builtins.concatStringsSep "\n" [
345 mypkgs.ludivinecassal_dev.apache.vhostConf
346 ];
347 })
348 (withConf "ludivinecassal" // {
349 hostName = "ludivinecassal.com";
350 serverAliases = [ "www.ludivinecassal.com" ];
351 documentRoot = mypkgs.ludivinecassal_prod.webRoot;
352 extraConfig = builtins.concatStringsSep "\n" [
353 mypkgs.ludivinecassal_prod.apache.vhostConf
354 ];
355 })
356 (withConf "eldiron" // {
357 hostName = "piedsjaloux.immae.eu";
358 documentRoot = mypkgs.piedsjaloux_dev.webRoot;
359 extraConfig = builtins.concatStringsSep "\n" [
360 mypkgs.piedsjaloux_dev.apache.vhostConf
361 ];
362 })
363 (withConf "piedsjaloux" // {
364 hostName = "piedsjaloux.fr";
365 serverAliases = [ "www.piedsjaloux.fr" ];
366 documentRoot = mypkgs.piedsjaloux_prod.webRoot;
367 extraConfig = builtins.concatStringsSep "\n" [
368 mypkgs.piedsjaloux_prod.apache.vhostConf
369 ];
370 })
371 (withConf "eldiron" // {
372 hostName = "chloe.immae.eu";
373 documentRoot = mypkgs.chloe_dev.webRoot;
374 extraConfig = builtins.concatStringsSep "\n" [
375 mypkgs.chloe_dev.apache.vhostConf
376 ];
377 })
378 (withConf "chloe" // {
379 hostName = "osteopathe-cc.fr";
380 serverAliases = [ "www.osteopathe-cc.fr" ];
381 documentRoot = mypkgs.chloe_prod.webRoot;
382 extraConfig = builtins.concatStringsSep "\n" [
383 mypkgs.chloe_prod.apache.vhostConf
384 ];
385 })
386 (withConf "eldiron" // {
387 hostName = "dev.aten.pro";
388 documentRoot = mypkgs.aten_dev.webRoot;
389 extraConfig = builtins.concatStringsSep "\n" [
390 mypkgs.aten_dev.apache.vhostConf
391 ];
392 })
393 (withConf "aten" // {
394 hostName = "aten.pro";
395 serverAliases = [ "www.aten.pro" ];
396 documentRoot = mypkgs.aten_prod.webRoot;
397 extraConfig = builtins.concatStringsSep "\n" [
398 mypkgs.aten_prod.apache.vhostConf
399 ];
400 })
401 (withConf "eldiron" // {
402 hostName = "cloud.immae.eu";
403 documentRoot = mypkgs.nextcloud.webRoot;
404 extraConfig = builtins.concatStringsSep "\n" [
405 mypkgs.nextcloud.apache.vhostConf
406 ];
407 })
408 (withConf "eldiron" // {
409 hostName = "git.immae.eu";
410 documentRoot = mypkgs.git.web.webRoot;
411 extraConfig = builtins.concatStringsSep "\n" [
412 mypkgs.git.web.apache.vhostConf
413 mypkgs.mantisbt.apache.vhostConf
414 ] + ''
415 RewriteEngine on
416 RewriteCond %{REQUEST_URI} ^/releases
417 RewriteRule /releases(.*) https://release.immae.eu$1 [P,L]
418 '';
419 })
420 { # Should go last, default fallback
421 listen = [ { ip = "*"; port = 80; } ];
422 hostName = "redirectSSL";
423 serverAliases = [ "*" ];
424 enableSSL = false;
425 logFormat = "combinedVhost";
426 documentRoot = "/var/lib/acme/acme-challenge";
427 extraConfig = ''
428 RewriteEngine on
429 RewriteCond "%{REQUEST_URI}" "!^/\.well-known"
430 RewriteRule ^(.+) https://%{HTTP_HOST}$1 [R=301]
431 # To redirect in specific "VirtualHost *:80", do
432 # RedirectMatch 301 ^/((?!\.well-known.*$).*)$ https://host/$1
433 # rather than rewrite
434 '';
435 }
436 ];
437 };
438
439 services.cron = {
440 enable = true;
441 systemCronJobs = let
442 stats = domain: conf: let
443 d = pkgs.writeScriptBin "stats-${domain}" ''
444 #!${pkgs.stdenv.shell}
445 set -e
446 shopt -s nullglob
447 date_regex=$(LC_ALL=C date -d yesterday +'%d\/%b\/%Y')
448 TMPFILE=$(mktemp)
449 trap "rm -f $TMPFILE" EXIT
450
451 cat /var/log/httpd/access_log-${domain} | sed -n "/\\[$date_regex/ p" > $TMPFILE
452 for i in /var/log/httpd/access_log-${domain}*.gz; do
453 zcat "$i" | sed -n "/\\[$date_regex/ p" >> $TMPFILE
454 done
455 goaccess $TMPFILE --no-progress -o /var/lib/goaccess/${domain}/index.html -p ${conf}
456 '';
457 in "${d}/bin/stats-${domain}";
458 # FIXME: running several goaccess simultaneously seems to be
459 # bugged?
460 in [
461 "5 0 * * * root ${stats "aten.pro" ./packages/aten_goaccess.conf}"
462 "6 0 * * * root ${stats "ludivinecassal.com" ./packages/ludivinecassal_goaccess.conf}"
463 "7 0 * * * root ${stats "piedsjaloux.fr" ./packages/piedsjaloux_goaccess.conf}"
464 "8 0 * * * root ${stats "osteopathe-cc.fr" ./packages/chloe_goaccess.conf}"
465 "9 0 * * * root ${stats "connexionswing.com" ./packages/connexionswing_goaccess.conf}"
466 ];
467 };
468
469 systemd.services.tt-rss = {
470 description = "Tiny Tiny RSS feeds update daemon";
471 serviceConfig = {
472 User = "wwwrun";
473 ExecStart = "${pkgs.php}/bin/php ${mypkgs.ttrss.webRoot}/update.php --daemon";
474 StandardOutput = "syslog";
475 StandardError = "syslog";
476 PermissionsStartOnly = true;
477 };
478
479 wantedBy = [ "multi-user.target" ];
480 requires = ["postgresql.service"];
481 after = ["network.target" "postgresql.service"];
482 };
483 };
484 }