]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - modules/private/system/quatresaisons.nix
491e215eb180e18d7925dc84245e090203ca338e
[perso/Immae/Config/Nix.git] / modules / private / system / quatresaisons.nix
1 { config, pkgs, lib, ... }:
2 let
3 serverSpecificConfig = config.myEnv.serverSpecific.quatresaisons;
4 yarnModules = pkgs.yarn2nix-moretea.mkYarnModules rec {
5 name = "landing";
6 pname = name;
7 version = "v1.0.0";
8 packageJSON = "${pkgs.sources.webapps-landing}/package.json";
9 yarnLock = "${pkgs.sources.webapps-landing}/yarn.lock";
10 yarnNix = ../websites/tools/tools/landing/yarn-packages.nix;
11 };
12 toLanding = landingConfig: pkgs.stdenv.mkDerivation rec {
13 pname = "landing";
14 version = "v1.0.0";
15 src = pkgs.sources.webapps-landing;
16
17 buildInputs = [ yarnModules pkgs.yarn2nix-moretea.yarn ];
18 configurePhase = ''
19 ln -s ${yarnModules}/node_modules .
20 '';
21 buildPhase = ''
22 yarn build
23 '';
24 installPhase = ''
25 cp -a dist $out
26 cp -f ${landingConfig} $out/config.yml
27 ln -s service-worker.js $out/worker.js
28 '';
29 };
30 normalUsers = serverSpecificConfig.users;
31 sponsoredUser = pkgs.writeScriptBin "sponsored_user" ''
32 #!/usr/bin/env bash
33
34 set -euo pipefail
35 [ -z "''${SUDO_USER+x}" ] && echo "Must be run with sudo" && exit 1
36
37 mygroup=$(id -ng $SUDO_USER)
38
39 sponsored=$(getent group $mygroup | cut -d':' -f4)
40
41 echo "Sponsored users: ''${sponsored:-<none>}"
42
43 log () {
44 touch /var/log/sponsored_users
45 chmod go-rwx /var/log/sponsored_users
46 echo "`date` $mygroup $1" | LANG=C cat -v | tr '\012' ' ' | sed 's:$:\x0a:' >> /var/log/sponsored_users
47 }
48
49 create_user () {
50 log "creates $1: $2"
51 useradd -m -G users,$mygroup -g $mygroup -p '!' "$1"
52 touch /var/lib/nixos/sponsored_users
53 chmod go-rwx /var/lib/nixos/sponsored_users
54 echo "$mygroup $1 $2" >> /var/lib/nixos/sponsored_users
55 (${pkgs.openldap}/bin/ldapadd -c -D cn=root,dc=salle-s,dc=org \
56 -y ${config.secrets.fullPaths."ldap/sync_password"} 2>/dev/null >/dev/null || true) <<EOF
57 dn: uid=$1,uid=$mygroup,ou=users,dc=salle-s,dc=org
58 objectClass: inetOrgPerson
59 cn: $1
60 description:: $(echo -n "$2" | base64)
61 sn: $1
62 uid: $1
63 EOF
64 while ! passwd "$1"; do
65 echo "please give an initial password"
66 done
67 }
68
69 delete_user () {
70 IFS=",";
71 for u in $sponsored; do
72 if [ "$u" = "$1" ]; then
73 log "deletes $1"
74 userdel -r "$1"
75 sed -i -e "/^$mygroup $1/d" /var/lib/nixos/sponsored_users
76 ${pkgs.openldap}/bin/ldapdelete -D cn=root,dc=salle-s,dc=org \
77 -y ${config.secrets.fullPaths."ldap/sync_password"} \
78 "uid=$1,uid=$mygroup,ou=users,dc=salle-s,dc=org"
79 echo "deleted"
80 exit 0
81 fi
82 done
83
84 echo "User does not exist or does not belong to you";
85 exit 1
86 }
87
88 reset_password () {
89 IFS=",";
90 for u in $sponsored; do
91 if [ "$u" = "$1" ]; then
92 log "resets password for $1"
93 passwd "$1"
94 exit 0
95 fi
96 done
97
98 echo "User does not exist or does not belong to you";
99 exit 1
100 }
101
102 reset_ldap_password () {
103 if [ "$1" = "$mygroup" ]; then
104 log "resets web password"
105 ${pkgs.openldap}/bin/ldappasswd -D cn=root,dc=salle-s,dc=org \
106 -y ${config.secrets.fullPaths."ldap/sync_password"} \
107 -S "uid=$mygroup,ou=users,dc=salle-s,dc=org"
108 else
109 IFS=",";
110 for u in $sponsored; do
111 if [ "$u" = "$1" ]; then
112 log "resets web password of $1"
113 ${pkgs.openldap}/bin/ldappasswd -D cn=root,dc=salle-s,dc=org \
114 -y ${config.secrets.fullPaths."ldap/sync_password"} \
115 -S "uid=$1,uid=$mygroup,ou=users,dc=salle-s,dc=org"
116 exit 0
117 fi
118 done
119
120 echo "User does not exist or does not belong to you";
121 exit 1
122 fi
123 }
124
125 show_help () {
126 echo "sponsored_users create username realname"
127 echo " create a new sub-user attached to your account"
128 echo "sponsored_users (delete|reset_password) username"
129 echo " delete a sub-user attached to your account or reset his password"
130 echo "sponsored_users reset_ldap_password username"
131 echo " reset the web password of a sub-user or yourself"
132 }
133
134 [ -z "''${1+x}" -o -z "''${2+x}" ] && { show_help ; exit 0; }
135 action="$1"
136 username="$2"
137 shift
138 shift
139
140 case "$action" in
141 create)
142 [ -z "''${1+x}" ] && { show_help ; echo "Conformément à la charte https://4c.salle-s.org/charte veuillez préciser le nom réel du futur utilisateur du compte $username, juste pour root." ; exit 1; }
143 create_user "$username" "$*";
144 ;;
145 delete)
146 delete_user "$username";
147 ;;
148 reset_password)
149 reset_password "$username";
150 ;;
151 reset_ldap_password)
152 reset_ldap_password "$username";
153 ;;
154 *)
155 show_help
156 ;;
157 esac
158 '';
159 in
160 {
161 deployment = {
162 targetUser = "root";
163 targetHost = config.hostEnv.ips.main.ip4;
164 substituteOnDestination = true;
165 };
166 # ssh-keyscan quatresaison | nix-shell -p ssh-to-age --run ssh-to-age
167 secrets.ageKeys = [ "age1yz8u6xvh2fltvyp96ep8crce3qx4tuceyhun6pwddfe0uvcrkarscxl7e7" ];
168
169 programs.ssh.package = pkgs.openssh.overrideAttrs(old: {
170 PATH_PASSWD_PROG = "/run/wrappers/bin/passwd";
171 buildFlags = [ "SSH_KEYSIGN=/run/wrappers/bin/ssh-keysign" ];
172 });
173
174 imports = builtins.attrValues (import ../..) ++
175 [ ./quatresaisons/nextcloud.nix ./quatresaisons/databases.nix ];
176
177 myEnv = import ../../../nixops/secrets/environment.nix;
178
179 fileSystems = {
180 "/" = { device = "/dev/disk/by-uuid/865931b4-c5cc-439f-8e42-8072c7a30634"; fsType = "ext4"; };
181 "/home" = { device = "/dev/disk/by-uuid/76020bc4-5b88-464c-8952-9a59072c597f"; fsType = "ext4"; neededForBoot = true; };
182 "/boot" = { device = "/dev/disk/by-uuid/0fb8421a-61e5-4ed5-a795-4dd3a9b2152a"; fsType = "ext4"; };
183 "/var/lib" = { device = "/home/var_lib"; fsType = "none"; options = [ "defaults,bind" ]; };
184 };
185 powerManagement.cpuFreqGovernor = "powersave";
186 hardware.enableRedistributableFirmware = true;
187
188 boot.initrd.availableKernelModules = [ "ahci" "megaraid_sas" "sd_mod" ];
189 boot.initrd.kernelModules = [ "dm-snapshot" ];
190 boot.kernelModules = [ "kvm-intel" ];
191
192 boot.loader.grub.enable = true;
193 boot.loader.grub.version = 2;
194 boot.loader.grub.device = "/dev/sda";
195
196 networking.firewall.enable = false;
197 networking.firewall.allowedTCPPorts = [ 80 443 ];
198 networking.useDHCP = false;
199 networking.interfaces.eth0.useDHCP = true;
200 networking.interfaces.eth0.ipv6.addresses = [
201 { address = pkgs.lib.head config.hostEnv.ips.main.ip6; prefixLength = 64; }
202 ];
203 networking.defaultGateway6 = { address = "fe80::1"; interface = "eth0"; };
204 services.udev.extraRules = ''
205 ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="c8:60:00:8b:2f:f0", NAME="eth0"
206 '';
207 security.pam.services.chage.text = ''
208 auth sufficient pam_rootok.so
209 auth required pam_unix.so
210 account required pam_unix.so
211 session required pam_unix.so
212 password required pam_permit.so
213 '';
214 security.pam.services.sshd.makeHomeDir = true;
215 security.pam.services.passwd_default = {};
216 security.pam.services.passwd.text = ''
217 password required pam_cracklib.so enforce_for_root difok=2 minlen=8 dcredit=2 ocredit=2 retry=3
218 '' + config.security.pam.services.passwd_default.text;
219
220 system.activationScripts.ldapSync = {
221 deps = [ "secrets" "users" ];
222 text =
223 let
224 com = "-D cn=root,dc=salle-s,dc=org -y ${config.secrets.fullPaths."ldap/sync_password"}";
225 in ''
226 # Add users
227 ${pkgs.openldap}/bin/ldapadd -c ${com} -f ${config.secrets.fullPaths."ldap/ldaptree.ldif"} 2>/dev/null >/dev/null || true
228
229 # Remove obsolete users
230 ${pkgs.openldap}/bin/ldapsearch -LLL ${com} -s one -b "ou=users,dc=salle-s,dc=org" "uid" |\
231 grep "^uid" | ${pkgs.gnused}/bin/sed -e "s/uid: //" | while read ldapuser; do
232
233 for user in ${builtins.concatStringsSep " " (builtins.attrNames normalUsers)}; do
234 if [ "$user" = "$ldapuser" ]; then
235 continue 2
236 fi
237 done
238 ${pkgs.openldap}/bin/ldapdelete -r ${com} uid=$ldapuser,ou=users,dc=salle-s,dc=org
239 done
240
241 # Subusers
242 if [ -f /var/lib/nixos/sponsored_users ]; then
243 cat /var/lib/nixos/sponsored_users | while read mainUser subUser name; do
244 (${pkgs.openldap}/bin/ldapadd -c ${com} 2>/dev/null >/dev/null || true) <<EOF
245 dn: uid=$subUser,uid=$mainUser,ou=users,dc=salle-s,dc=org
246 objectClass: inetOrgPerson
247 cn: $subUser
248 description:: $(echo -n "$name" | base64)
249 sn: $subUser
250 uid: $subUser
251 EOF
252 done
253 fi
254 '';
255 };
256
257 secrets.keys = [
258 {
259 dest = "ldap/sync_password";
260 permissions = "0400";
261 text = serverSpecificConfig.ldap_sync_password;
262 }
263 {
264 dest = "ldap/ldaptree.ldif";
265 permissions = "0400";
266 text = serverSpecificConfig.ldap_service_users
267 + (builtins.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
268 dn: uid=${n},ou=users,dc=salle-s,dc=org
269 objectClass: inetOrgPerson
270 cn: ${n}
271 description: ${v._meta.name or n} ${v._meta.email}
272 sn: ${n}
273 uid: ${n}
274 '') normalUsers));
275 }
276 ];
277
278 myServices.monitoring.enable = true;
279 myServices.certificates.enable = true;
280 users.mutableUsers = true;
281 system.stateVersion = "21.03";
282 programs.zsh.enable = true;
283
284 users.motd = ''
285 Bienvenue sur quatresaisons.salle-s.org !
286
287 * Charte :
288 https://4c.salle-s.org/charte
289 * Gérer les utilisateurs unix additionnels :
290 sudo sponsored_user -h
291 * Applications web :
292 * tableau de bord : https://4c.salle-s.org/
293 * nextcloud : https://nextcloud.4c.salle-s.org/
294 '';
295
296 users.groups =
297 lib.mapAttrs (n: v: { gid = v.uid; }) normalUsers
298 // { wwwrun = { gid = config.ids.gids.wwwrun; }; };
299 users.users =
300 let
301 defaultNormal = n: {
302 group = n;
303 extraGroups = [ "users" ];
304 isNormalUser = true;
305 };
306 in
307 lib.mapAttrs (n: v: defaultNormal n // (lib.filterAttrs (k: _: k != "_meta") v)) normalUsers
308 // {
309 sponsored-separator = {
310 uid = 10000;
311 group = "users";
312 home = "/var/empty";
313 extraGroups = [];
314 isNormalUser = true;
315 createHome = false;
316 };
317 wwwrun = {
318 group = "wwwrun";
319 description = "Apache httpd user";
320 uid = config.ids.uids.wwwrun;
321 extraGroups = [ "keys" ];
322 };
323 };
324
325 system.activationScripts.usersPost = {
326 deps = [ "users" "groups" ];
327 text = builtins.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
328 if getent shadow "${n}" | grep -q '^${n}:${v.initialHashedPassword or "!"}:1'; then
329 chage -d 0 "${n}"
330 [ '${v.initialHashedPassword or "!"}' = '!' ] && passwd -d "${n}"
331 fi
332 '') normalUsers);
333 };
334 security.sudo.extraRules = [
335 {
336 commands = [
337 { command = "${sponsoredUser}/bin/sponsored_user"; options = [ "NOPASSWD" ]; }
338 { command = "/run/current-system/sw/bin/sponsored_user"; options = [ "NOPASSWD" ]; }
339 ];
340 users = builtins.attrNames normalUsers;
341 runAs = "root";
342 }
343 ];
344
345 environment.systemPackages = [
346 sponsoredUser
347 pkgs.git pkgs.vim pkgs.rsync pkgs.strace pkgs.home-manager
348 pkgs.telnet pkgs.htop pkgs.iftop pkgs.bind.dnsutils pkgs.httpie
349 pkgs.iotop pkgs.whois pkgs.ngrep pkgs.tcpdump pkgs.tshark
350 pkgs.tcpflow pkgs.nmap pkgs.p0f pkgs.socat pkgs.lsof pkgs.psmisc
351 pkgs.openssl pkgs.wget pkgs.pv pkgs.smartmontools pkgs.youtube-dl
352 pkgs.unzip pkgs.octave pkgs.feh pkgs.xv pkgs.sshfs pkgs.gdb
353 pkgs.file pkgs.lynx pkgs.tmux pkgs.awesome pkgs.libreoffice
354 pkgs.evince pkgs.firefox pkgs.xcalib pkgs.python3 pkgs.python2
355 pkgs.xorg.xkbcomp pkgs.subversion pkgs.xclip pkgs.imagemagick
356 pkgs.bc pkgs.sox pkgs.zip pkgs.gnome3.gnome-screenshot
357 pkgs.datadog-process-agent
358 ];
359
360 services.websites.env.production = {
361 enable = true;
362 adminAddr = "httpd@immae.eu";
363 httpdName = "Prod";
364 modules = [ "http2" "deflate" "filter" ];
365 extraConfig = [
366 ''
367 LogFormat "%{Host}i:%p %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedVhost
368 Protocols h2 http/1.1
369 AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
370 '' ];
371 ips =
372 let ips = config.hostEnv.ips.main;
373 in [ips.ip4] ++ (ips.ip6 or []);
374
375 fallbackVhost = {
376 certName = "quatresaisons";
377 hosts = [ "quatresaisons.immae.eu" ];
378 root = pkgs.runCommand "empty" {} "mkdir $out && touch $out/index.html";
379 extraConfig = [ "DirectoryIndex index.html" ];
380 };
381 vhostConfs.salle-s = {
382 certName = "quatresaisons";
383 addToCerts = true;
384 hosts = [ "salle-s.org" ];
385 root = toLanding ./quatresaisons/landing.yml;
386 extraConfig = [
387 ''
388 <Directory ${toLanding ./quatresaisons/landing.yml}>
389 AllowOverride None
390 Require all granted
391 DirectoryIndex index.html
392 </Directory>
393 ''
394 ];
395 };
396 vhostConfs.tools = {
397 certName = "quatresaisons";
398 addToCerts = true;
399 hosts = [ "4c.salle-s.org" "quatresaisons.salle-s.org" "quatre-saisons.salle-s.org" ];
400 root = toLanding ./quatresaisons/landing_4c.yml;
401 extraConfig = [
402 ''
403 Alias /charte ${serverSpecificConfig.charte_path}
404 <Directory ${serverSpecificConfig.charte_path}>
405 AllowOverride None
406 Require all granted
407 DirectoryIndex index.html index.txt
408 </Directory>
409
410 <Directory ${toLanding ./quatresaisons/landing_4c.yml}>
411 AllowOverride None
412 Require all granted
413 DirectoryIndex index.html
414 </Directory>
415 ''
416 ];
417 };
418 };
419 system.activationScripts.httpd = ''
420 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php
421 install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions
422 '';
423
424 services.phpfpm = {
425 phpOptions = ''
426 session.save_path = "/var/lib/php/sessions"
427 post_max_size = 20M
428 ; 15 days (seconds)
429 session.gc_maxlifetime = 1296000
430 ; 30 days (minutes)
431 session.cache_expire = 43200
432 '';
433 settings = {
434 log_level = "notice";
435 };
436 };
437
438 }