]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - virtual/eldiron.nix
Start moving websites to their own modules: certificates
[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 connexionswing_dev = mypkgs.connexionswing_dev.phpFpm.pool;
137 connexionswing_prod = mypkgs.connexionswing_prod.phpFpm.pool;
138 ludivinecassal_dev = mypkgs.ludivinecassal_dev.phpFpm.pool;
139 ludivinecassal_prod = mypkgs.ludivinecassal_prod.phpFpm.pool;
140 piedsjaloux_dev = mypkgs.piedsjaloux_dev.phpFpm.pool;
141 piedsjaloux_prod = mypkgs.piedsjaloux_prod.phpFpm.pool;
142 chloe_dev = mypkgs.chloe_dev.phpFpm.pool;
143 chloe_prod = mypkgs.chloe_prod.phpFpm.pool;
144 aten_dev = mypkgs.aten_dev.phpFpm.pool;
145 aten_prod = mypkgs.aten_prod.phpFpm.pool;
146 nextcloud = mypkgs.nextcloud.phpFpm.pool;
147 mantisbt = mypkgs.mantisbt.phpFpm.pool;
148 ttrss = mypkgs.ttrss.phpFpm.pool;
149 roundcubemail = mypkgs.roundcubemail.phpFpm.pool;
150 davical = mypkgs.davical.phpFpm.pool;
151 };
152 };
153
154 system.activationScripts = {
155 connexionswing_dev = mypkgs.connexionswing_dev.activationScript;
156 connexionswing_prod = mypkgs.connexionswing_prod.activationScript;
157 ludivinecassal_dev = mypkgs.ludivinecassal_dev.activationScript;
158 ludivinecassal_prod = mypkgs.ludivinecassal_prod.activationScript;
159 piedsjaloux_dev = mypkgs.piedsjaloux_dev.activationScript;
160 piedsjaloux_prod = mypkgs.piedsjaloux_prod.activationScript;
161 chloe_dev = mypkgs.chloe_dev.activationScript;
162 chloe_prod = mypkgs.chloe_prod.activationScript;
163 aten_dev = mypkgs.aten_dev.activationScript;
164 aten_prod = mypkgs.aten_prod.activationScript;
165 nextcloud = mypkgs.nextcloud.activationScript;
166 ttrss = mypkgs.ttrss.activationScript;
167 roundcubemail = mypkgs.roundcubemail.activationScript;
168 httpd = ''
169 install -d -m 0755 /var/lib/acme/acme-challenge
170 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions
171 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/adminer
172 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/mantisbt
173 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/ttrss
174 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions/davical
175 '';
176 redis = ''
177 mkdir -p /run/redis
178 chown redis /run/redis
179 '';
180 # FIXME: initial sync
181 goaccess = ''
182 mkdir -p /var/lib/goaccess
183 mkdir -p /var/lib/goaccess/aten.pro
184 mkdir -p /var/lib/goaccess/ludivinecassal.com
185 mkdir -p /var/lib/goaccess/piedsjaloux.fr
186 mkdir -p /var/lib/goaccess/osteopathe-cc.fr
187 mkdir -p /var/lib/goaccess/connexionswing.com
188 '';
189 };
190
191 environment.etc."ssh/ldap_authorized_keys" = let
192 ldap_authorized_keys =
193 assert checkEnv "NIXOPS_SSHD_LDAP_PASSWORD";
194 wrap {
195 name = "ldap_authorized_keys";
196 file = ./ldap_authorized_keys.sh;
197 vars = {
198 LDAP_PASS = builtins.getEnv "NIXOPS_SSHD_LDAP_PASSWORD";
199 GITOLITE_SHELL = "${pkgs.gitolite}/bin/gitolite-shell";
200 ECHO = "${pkgs.coreutils}/bin/echo";
201 };
202 paths = [ pkgs.openldap pkgs.stdenv.shellPackage pkgs.gnugrep pkgs.gnused pkgs.coreutils ];
203 };
204 in {
205 enable = true;
206 mode = "0755";
207 user = "root";
208 source = ldap_authorized_keys;
209 };
210
211 services.gitDaemon = {
212 enable = true;
213 user = "gitolite";
214 group = "gitolite";
215 basePath = "${mypkgs.git.web.varDir}/repositories";
216 };
217
218 # FIXME: logrotate
219 services.httpd = let
220 withConf = domain: {
221 enableSSL = true;
222 sslServerCert = "/var/lib/acme/${domain}/cert.pem";
223 sslServerKey = "/var/lib/acme/${domain}/key.pem";
224 sslServerChain = "/var/lib/acme/${domain}/fullchain.pem";
225 logFormat = "combinedVhost";
226 listen = [ { ip = "*"; port = 443; } ];
227 };
228 apacheConfig = {
229 gzip = {
230 modules = [ "deflate" "filter" ];
231 extraConfig = ''
232 AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
233 '';
234 };
235 ldap = {
236 modules = [ "ldap" "authnz_ldap" ];
237 extraConfig = assert checkEnv "NIXOPS_HTTP_LDAP_PASSWORD"; ''
238 <IfModule ldap_module>
239 LDAPSharedCacheSize 500000
240 LDAPCacheEntries 1024
241 LDAPCacheTTL 600
242 LDAPOpCacheEntries 1024
243 LDAPOpCacheTTL 600
244 </IfModule>
245
246 <Macro LDAPConnect>
247 <IfModule authnz_ldap_module>
248 AuthLDAPURL ldap://ldap.immae.eu:389/dc=immae,dc=eu
249 AuthLDAPBindDN cn=httpd,ou=services,dc=immae,dc=eu
250 AuthLDAPBindPassword "${builtins.getEnv "NIXOPS_HTTP_LDAP_PASSWORD"}"
251 AuthType Basic
252 AuthName "Authentification requise (Acces LDAP)"
253 AuthBasicProvider ldap
254 </IfModule>
255 </Macro>
256
257 <Macro Stats %{domain}>
258 Alias /awstats /var/lib/goaccess/%{domain}
259 <Directory /var/lib/goaccess/%{domain}>
260 DirectoryIndex index.html
261 AllowOverride None
262 Require all granted
263 </Directory>
264 <Location /awstats>
265 Use LDAPConnect
266 Require ldap-group cn=%{domain},ou=stats,cn=httpd,ou=services,dc=immae,dc=eu
267 </Location>
268 </Macro>
269 '';
270 };
271 http2 = {
272 modules = [ "http2" ];
273 extraConfig = ''
274 Protocols h2 http/1.1
275 '';
276 };
277 customLog = {
278 modules = [];
279 extraConfig = ''
280 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedVhost
281 '';
282 };
283 };
284 in rec {
285 enable = true;
286 logPerVirtualHost = true;
287 multiProcessingModule = "worker";
288 adminAddr = "httpd@immae.eu";
289 logFormat = "combinedVhost";
290 extraModules = pkgs.lib.lists.unique (
291 mypkgs.adminer.apache.modules ++
292 mypkgs.nextcloud.apache.modules ++
293 mypkgs.connexionswing_dev.apache.modules ++
294 mypkgs.connexionswing_prod.apache.modules ++
295 mypkgs.ludivinecassal_dev.apache.modules ++
296 mypkgs.ludivinecassal_prod.apache.modules ++
297 mypkgs.piedsjaloux_dev.apache.modules ++
298 mypkgs.piedsjaloux_prod.apache.modules ++
299 mypkgs.chloe_dev.apache.modules ++
300 mypkgs.chloe_prod.apache.modules ++
301 mypkgs.aten_dev.apache.modules ++
302 mypkgs.aten_prod.apache.modules ++
303 mypkgs.ympd.apache.modules ++
304 mypkgs.git.web.apache.modules ++
305 mypkgs.mantisbt.apache.modules ++
306 mypkgs.ttrss.apache.modules ++
307 mypkgs.roundcubemail.apache.modules ++
308 pkgs.lib.lists.flatten (pkgs.lib.attrsets.mapAttrsToList (n: v: v.modules) apacheConfig) ++
309 [ "macro" ]);
310 extraConfig = builtins.concatStringsSep "\n"
311 (pkgs.lib.attrsets.mapAttrsToList (n: v: v.extraConfig) apacheConfig);
312 virtualHosts = [
313 (withConf "eldiron" // {
314 hostName = "eldiron.immae.eu";
315 documentRoot = ./www;
316 extraConfig = ''
317 DirectoryIndex index.htm
318 '';
319 })
320 (withConf "eldiron" // {
321 hostName = "db-1.immae.eu";
322 documentRoot = null;
323 extraConfig = builtins.concatStringsSep "\n" [
324 mypkgs.adminer.apache.vhostConf
325 ];
326 })
327 (withConf "eldiron" // {
328 hostName = "tools.immae.eu";
329 documentRoot = null;
330 extraConfig = builtins.concatStringsSep "\n" [
331 mypkgs.adminer.apache.vhostConf
332 mypkgs.ympd.apache.vhostConf
333 mypkgs.ttrss.apache.vhostConf
334 mypkgs.roundcubemail.apache.vhostConf
335 ];
336 })
337 (withConf "eldiron" // {
338 hostName = "dav.immae.eu";
339 documentRoot = null;
340 extraConfig = builtins.concatStringsSep "\n" [
341 mypkgs.infcloud.apache.vhostConf
342 mypkgs.davical.apache.vhostConf
343 ];
344 })
345 (withConf "eldiron" // {
346 hostName = "connexionswing.immae.eu";
347 serverAliases = [ "sandetludo.immae.eu" ];
348 documentRoot = mypkgs.connexionswing_dev.webRoot;
349 extraConfig = builtins.concatStringsSep "\n" [
350 mypkgs.connexionswing_dev.apache.vhostConf
351 ];
352 })
353 (withConf "connexionswing" // {
354 hostName = "connexionswing.com";
355 serverAliases = [ "sandetludo.com" "www.connexionswing.com" "www.sandetludo.com" ];
356 documentRoot = mypkgs.connexionswing_prod.webRoot;
357 extraConfig = builtins.concatStringsSep "\n" [
358 mypkgs.connexionswing_prod.apache.vhostConf
359 ];
360 })
361 (withConf "eldiron" // {
362 hostName = "ludivine.immae.eu";
363 documentRoot = mypkgs.ludivinecassal_dev.webRoot;
364 extraConfig = builtins.concatStringsSep "\n" [
365 mypkgs.ludivinecassal_dev.apache.vhostConf
366 ];
367 })
368 (withConf "ludivinecassal" // {
369 hostName = "ludivinecassal.com";
370 serverAliases = [ "www.ludivinecassal.com" ];
371 documentRoot = mypkgs.ludivinecassal_prod.webRoot;
372 extraConfig = builtins.concatStringsSep "\n" [
373 mypkgs.ludivinecassal_prod.apache.vhostConf
374 ];
375 })
376 (withConf "eldiron" // {
377 hostName = "piedsjaloux.immae.eu";
378 documentRoot = mypkgs.piedsjaloux_dev.webRoot;
379 extraConfig = builtins.concatStringsSep "\n" [
380 mypkgs.piedsjaloux_dev.apache.vhostConf
381 ];
382 })
383 (withConf "piedsjaloux" // {
384 hostName = "piedsjaloux.fr";
385 serverAliases = [ "www.piedsjaloux.fr" ];
386 documentRoot = mypkgs.piedsjaloux_prod.webRoot;
387 extraConfig = builtins.concatStringsSep "\n" [
388 mypkgs.piedsjaloux_prod.apache.vhostConf
389 ];
390 })
391 (withConf "eldiron" // {
392 hostName = "chloe.immae.eu";
393 documentRoot = mypkgs.chloe_dev.webRoot;
394 extraConfig = builtins.concatStringsSep "\n" [
395 mypkgs.chloe_dev.apache.vhostConf
396 ];
397 })
398 (withConf "chloe" // {
399 hostName = "osteopathe-cc.fr";
400 serverAliases = [ "www.osteopathe-cc.fr" ];
401 documentRoot = mypkgs.chloe_prod.webRoot;
402 extraConfig = builtins.concatStringsSep "\n" [
403 mypkgs.chloe_prod.apache.vhostConf
404 ];
405 })
406 (withConf "eldiron" // {
407 hostName = "dev.aten.pro";
408 documentRoot = mypkgs.aten_dev.webRoot;
409 extraConfig = builtins.concatStringsSep "\n" [
410 mypkgs.aten_dev.apache.vhostConf
411 ];
412 })
413 (withConf "aten" // {
414 hostName = "aten.pro";
415 serverAliases = [ "www.aten.pro" ];
416 documentRoot = mypkgs.aten_prod.webRoot;
417 extraConfig = builtins.concatStringsSep "\n" [
418 mypkgs.aten_prod.apache.vhostConf
419 ];
420 })
421 (withConf "eldiron" // {
422 hostName = "cloud.immae.eu";
423 documentRoot = mypkgs.nextcloud.webRoot;
424 extraConfig = builtins.concatStringsSep "\n" [
425 mypkgs.nextcloud.apache.vhostConf
426 ];
427 })
428 (withConf "eldiron" // {
429 hostName = "git.immae.eu";
430 documentRoot = mypkgs.git.web.webRoot;
431 extraConfig = builtins.concatStringsSep "\n" [
432 mypkgs.git.web.apache.vhostConf
433 mypkgs.mantisbt.apache.vhostConf
434 ] + ''
435 RewriteEngine on
436 RewriteCond %{REQUEST_URI} ^/releases
437 RewriteRule /releases(.*) https://release.immae.eu$1 [P,L]
438 '';
439 })
440 { # Should go last, default fallback
441 listen = [ { ip = "*"; port = 80; } ];
442 hostName = "redirectSSL";
443 serverAliases = [ "*" ];
444 enableSSL = false;
445 logFormat = "combinedVhost";
446 documentRoot = "/var/lib/acme/acme-challenge";
447 extraConfig = ''
448 RewriteEngine on
449 RewriteCond "%{REQUEST_URI}" "!^/\.well-known"
450 RewriteRule ^(.+) https://%{HTTP_HOST}$1 [R=301]
451 # To redirect in specific "VirtualHost *:80", do
452 # RedirectMatch 301 ^/((?!\.well-known.*$).*)$ https://host/$1
453 # rather than rewrite
454 '';
455 }
456 ];
457 };
458
459 services.cron = {
460 enable = true;
461 systemCronJobs = let
462 stats = domain: conf: let
463 d = pkgs.writeScriptBin "stats-${domain}" ''
464 #!${pkgs.stdenv.shell}
465 set -e
466 shopt -s nullglob
467 date_regex=$(LC_ALL=C date -d yesterday +'%d\/%b\/%Y')
468 TMPFILE=$(mktemp)
469 trap "rm -f $TMPFILE" EXIT
470
471 cat /var/log/httpd/access_log-${domain} | sed -n "/\\[$date_regex/ p" > $TMPFILE
472 for i in /var/log/httpd/access_log-${domain}*.gz; do
473 zcat "$i" | sed -n "/\\[$date_regex/ p" >> $TMPFILE
474 done
475 goaccess $TMPFILE --no-progress -o /var/lib/goaccess/${domain}/index.html -p ${conf}
476 '';
477 in "${d}/bin/stats-${domain}";
478 # FIXME: running several goaccess simultaneously seems to be
479 # bugged?
480 in [
481 "5 0 * * * root ${stats "aten.pro" ./packages/aten_goaccess.conf}"
482 "6 0 * * * root ${stats "ludivinecassal.com" ./packages/ludivinecassal_goaccess.conf}"
483 "7 0 * * * root ${stats "piedsjaloux.fr" ./packages/piedsjaloux_goaccess.conf}"
484 "8 0 * * * root ${stats "osteopathe-cc.fr" ./packages/chloe_goaccess.conf}"
485 "9 0 * * * root ${stats "connexionswing.com" ./packages/connexionswing_goaccess.conf}"
486 ];
487 };
488
489 systemd.services.tt-rss = {
490 description = "Tiny Tiny RSS feeds update daemon";
491 serviceConfig = {
492 User = "wwwrun";
493 ExecStart = "${pkgs.php}/bin/php ${mypkgs.ttrss.webRoot}/update.php --daemon";
494 StandardOutput = "syslog";
495 StandardError = "syslog";
496 PermissionsStartOnly = true;
497 };
498
499 wantedBy = [ "multi-user.target" ];
500 requires = ["postgresql.service"];
501 after = ["network.target" "postgresql.service"];
502 };
503 };
504 }