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