{ privateFiles }:
{ config, pkgs, lib, ... }:
let
serverSpecificConfig = config.myEnv.serverSpecific.quatresaisons;
yarnModules = pkgs.yarn2nix-moretea.mkYarnModules rec {
name = "landing";
pname = name;
version = "v1.0.0";
packageJSON = "${pkgs.sources.webapps-landing}/package.json";
yarnLock = "${pkgs.sources.webapps-landing}/yarn.lock";
yarnNix = ../websites/tools/tools/landing/yarn-packages.nix;
};
toLanding = landingConfig: pkgs.stdenv.mkDerivation rec {
pname = "landing";
version = "v1.0.0";
src = pkgs.sources.webapps-landing;
buildInputs = [ yarnModules pkgs.yarn2nix-moretea.yarn ];
configurePhase = ''
ln -s ${yarnModules}/node_modules .
'';
buildPhase = ''
yarn build
'';
installPhase = ''
cp -a dist $out
cp -f ${landingConfig} $out/config.yml
ln -s service-worker.js $out/worker.js
'';
};
normalUsers = serverSpecificConfig.users;
sponsoredUser = pkgs.writeScriptBin "sponsored_user" ''
#!/usr/bin/env bash
set -euo pipefail
[ -z "''${SUDO_USER+x}" ] && echo "Must be run with sudo" && exit 1
mygroup=$(id -ng $SUDO_USER)
sponsored=$(getent group $mygroup | cut -d':' -f4)
echo "Sponsored users: ''${sponsored:-<none>}"
log () {
touch /var/log/sponsored_users
chmod go-rwx /var/log/sponsored_users
echo "`date` $mygroup $1" | LANG=C cat -v | tr '\012' ' ' | sed 's:$:\x0a:' >> /var/log/sponsored_users
}
create_user () {
log "creates $1: $2"
useradd -m -G users,$mygroup -g $mygroup -p '!' "$1"
touch /var/lib/nixos/sponsored_users
chmod go-rwx /var/lib/nixos/sponsored_users
echo "$mygroup $1 $2" >> /var/lib/nixos/sponsored_users
(${pkgs.openldap}/bin/ldapadd -c -D cn=root,dc=salle-s,dc=org \
-y /var/secrets/ldap/sync_password 2>/dev/null >/dev/null || true) <<EOF
dn: uid=$1,uid=$mygroup,ou=users,dc=salle-s,dc=org
objectClass: inetOrgPerson
cn: $1
description:: $(echo -n "$2" | base64)
sn: $1
uid: $1
EOF
while ! passwd "$1"; do
echo "please give an initial password"
done
}
delete_user () {
IFS=",";
for u in $sponsored; do
if [ "$u" = "$1" ]; then
log "deletes $1"
userdel -r "$1"
sed -i -e "/^$mygroup $1/d" /var/lib/nixos/sponsored_users
${pkgs.openldap}/bin/ldapdelete -D cn=root,dc=salle-s,dc=org \
-y /var/secrets/ldap/sync_password \
"uid=$1,uid=$mygroup,ou=users,dc=salle-s,dc=org"
echo "deleted"
exit 0
fi
done
echo "User does not exist or does not belong to you";
exit 1
}
reset_password () {
IFS=",";
for u in $sponsored; do
if [ "$u" = "$1" ]; then
log "resets password for $1"
passwd "$1"
exit 0
fi
done
echo "User does not exist or does not belong to you";
exit 1
}
reset_ldap_password () {
if [ "$1" = "$mygroup" ]; then
log "resets web password"
${pkgs.openldap}/bin/ldappasswd -D cn=root,dc=salle-s,dc=org \
-y /var/secrets/ldap/sync_password \
-S "uid=$mygroup,ou=users,dc=salle-s,dc=org"
else
IFS=",";
for u in $sponsored; do
if [ "$u" = "$1" ]; then
log "resets web password of $1"
${pkgs.openldap}/bin/ldappasswd -D cn=root,dc=salle-s,dc=org \
-y /var/secrets/ldap/sync_password \
-S "uid=$1,uid=$mygroup,ou=users,dc=salle-s,dc=org"
exit 0
fi
done
echo "User does not exist or does not belong to you";
exit 1
fi
}
show_help () {
echo "sponsored_users create username realname"
echo " create a new sub-user attached to your account"
echo "sponsored_users (delete|reset_password) username"
echo " delete a sub-user attached to your account or reset his password"
echo "sponsored_users reset_ldap_password username"
echo " reset the web password of a sub-user or yourself"
}
[ -z "''${1+x}" -o -z "''${2+x}" ] && { show_help ; exit 0; }
action="$1"
username="$2"
shift
shift
case "$action" in
create)
[ -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; }
create_user "$username" "$*";
;;
delete)
delete_user "$username";
;;
reset_password)
reset_password "$username";
;;
reset_ldap_password)
reset_ldap_password "$username";
;;
*)
show_help
;;
esac
'';
in
{
deployment = {
targetUser = "root";
targetHost = config.hostEnv.ips.main.ip4;
substituteOnDestination = true;
};
programs.ssh.package = pkgs.openssh.overrideAttrs(old: {
PATH_PASSWD_PROG = "/run/wrappers/bin/passwd";
buildFlags = [ "SSH_KEYSIGN=/run/wrappers/bin/ssh-keysign" ];
});
imports = builtins.attrValues (import ../..) ++
[ ./quatresaisons/nextcloud.nix ./quatresaisons/databases.nix ];
myEnv = import "${privateFiles}/environment.nix" // { inherit privateFiles; };
fileSystems = {
"/" = { device = "/dev/disk/by-uuid/865931b4-c5cc-439f-8e42-8072c7a30634"; fsType = "ext4"; };
"/home" = { device = "/dev/disk/by-uuid/76020bc4-5b88-464c-8952-9a59072c597f"; fsType = "ext4"; neededForBoot = true; };
"/boot" = { device = "/dev/disk/by-uuid/0fb8421a-61e5-4ed5-a795-4dd3a9b2152a"; fsType = "ext4"; };
"/var/lib" = { device = "/home/var_lib"; fsType = "none"; options = [ "defaults,bind" ]; };
};
powerManagement.cpuFreqGovernor = "powersave";
hardware.enableRedistributableFirmware = true;
boot.initrd.availableKernelModules = [ "ahci" "megaraid_sas" "sd_mod" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-intel" ];
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
boot.loader.grub.device = "/dev/sda";
networking.firewall.enable = false;
networking.firewall.allowedTCPPorts = [ 80 443 ];
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
networking.interfaces.eth0.ipv6.addresses = [
{ address = pkgs.lib.head config.hostEnv.ips.main.ip6; prefixLength = 64; }
];
networking.defaultGateway6 = { address = "fe80::1"; interface = "eth0"; };
services.udev.extraRules = ''
ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="c8:60:00:8b:2f:f0", NAME="eth0"
'';
security.pam.services.chage.text = ''
auth sufficient pam_rootok.so
auth required pam_unix.so
account required pam_unix.so
session required pam_unix.so
password required pam_permit.so
'';
security.pam.services.sshd.makeHomeDir = true;
security.pam.services.passwd_default = {};
security.pam.services.passwd.text = ''
password required pam_cracklib.so enforce_for_root difok=2 minlen=8 dcredit=2 ocredit=2 retry=3
'' + config.security.pam.services.passwd_default.text;
system.activationScripts.ldapSync = {
deps = [ "secrets" "users" ];
text =
let
com = "-D cn=root,dc=salle-s,dc=org -y /var/secrets/ldap/sync_password";
in ''
# Add users
${pkgs.openldap}/bin/ldapadd -c ${com} -f /var/secrets/ldap/ldaptree.ldif 2>/dev/null >/dev/null || true
# Remove obsolete users
${pkgs.openldap}/bin/ldapsearch -LLL ${com} -s one -b "ou=users,dc=salle-s,dc=org" "uid" |\
grep "^uid" | ${pkgs.gnused}/bin/sed -e "s/uid: //" | while read ldapuser; do
for user in ${builtins.concatStringsSep " " (builtins.attrNames normalUsers)}; do
if [ "$user" = "$ldapuser" ]; then
continue 2
fi
done
${pkgs.openldap}/bin/ldapdelete -r ${com} uid=$ldapuser,ou=users,dc=salle-s,dc=org
done
# Subusers
if [ -f /var/lib/nixos/sponsored_users ]; then
cat /var/lib/nixos/sponsored_users | while read mainUser subUser name; do
(${pkgs.openldap}/bin/ldapadd -c ${com} 2>/dev/null >/dev/null || true) <<EOF
dn: uid=$subUser,uid=$mainUser,ou=users,dc=salle-s,dc=org
objectClass: inetOrgPerson
cn: $subUser
description:: $(echo -n "$name" | base64)
sn: $subUser
uid: $subUser
EOF
done
fi
'';
};
secrets.keys = [
{
dest = "ldap/sync_password";
permissions = "0400";
text = serverSpecificConfig.ldap_sync_password;
}
{
dest = "ldap/ldaptree.ldif";
permissions = "0400";
text = serverSpecificConfig.ldap_service_users
+ (builtins.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
dn: uid=${n},ou=users,dc=salle-s,dc=org
objectClass: inetOrgPerson
cn: ${n}
description: ${v._meta.name or n} ${v._meta.email}
sn: ${n}
uid: ${n}
'') normalUsers));
}
];
myServices.monitoring.enable = true;
myServices.certificates.enable = true;
users.mutableUsers = true;
system.stateVersion = "21.03";
programs.zsh.enable = true;
users.motd = ''
Bienvenue sur quatresaisons.salle-s.org !
* Charte :
https://4c.salle-s.org/charte
* Gérer les utilisateurs unix additionnels :
sudo sponsored_user -h
* Applications web :
* tableau de bord : https://4c.salle-s.org/
* nextcloud : https://nextcloud.4c.salle-s.org/
'';
users.groups =
lib.mapAttrs (n: v: { gid = v.uid; }) normalUsers
// { wwwrun = { gid = config.ids.gids.wwwrun; }; };
users.users =
let
defaultNormal = n: {
group = n;
extraGroups = [ "users" ];
isNormalUser = true;
};
in
lib.mapAttrs (n: v: defaultNormal n // (lib.filterAttrs (k: _: k != "_meta") v)) normalUsers
// {
sponsored-separator = {
uid = 10000;
group = "users";
home = "/var/empty";
extraGroups = [];
isNormalUser = true;
createHome = false;
};
wwwrun = {
group = "wwwrun";
description = "Apache httpd user";
uid = config.ids.uids.wwwrun;
extraGroups = [ "keys" ];
};
};
system.activationScripts.usersPost = {
deps = [ "users" "groups" ];
text = builtins.concatStringsSep "\n" (lib.mapAttrsToList (n: v: ''
if getent shadow "${n}" | grep -q '^${n}:${v.initialHashedPassword or "!"}:1'; then
chage -d 0 "${n}"
[ '${v.initialHashedPassword or "!"}' = '!' ] && passwd -d "${n}"
fi
'') normalUsers);
};
security.sudo.extraRules = [
{
commands = [
{ command = "${sponsoredUser}/bin/sponsored_user"; options = [ "NOPASSWD" ]; }
];
users = builtins.attrNames normalUsers;
runAs = "root";
}
];
environment.systemPackages = [
sponsoredUser
pkgs.git pkgs.vim pkgs.rsync pkgs.strace pkgs.home-manager
pkgs.telnet pkgs.htop pkgs.iftop pkgs.bind.dnsutils pkgs.httpie
pkgs.iotop pkgs.whois pkgs.ngrep pkgs.tcpdump pkgs.tshark
pkgs.tcpflow pkgs.nmap pkgs.p0f pkgs.socat pkgs.lsof pkgs.psmisc
pkgs.openssl pkgs.wget pkgs.pv pkgs.smartmontools pkgs.youtube-dl
pkgs.unzip pkgs.octave pkgs.feh pkgs.xv pkgs.sshfs pkgs.gdb
pkgs.file pkgs.lynx pkgs.tmux pkgs.awesome pkgs.libreoffice
pkgs.evince pkgs.firefox pkgs.xcalib pkgs.python3 pkgs.python2
pkgs.xorg.xkbcomp pkgs.subversion pkgs.xclip pkgs.imagemagick
pkgs.bc pkgs.sox pkgs.zip pkgs.gnome3.gnome-screenshot
pkgs.datadog-process-agent
];
services.websites.env.production = {
enable = true;
adminAddr = "httpd@immae.eu";
httpdName = "Prod";
modules = [ "http2" "deflate" "filter" ];
extraConfig = [
''
LogFormat "%{Host}i:%p %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedVhost
Protocols h2 http/1.1
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
'' ];
ips =
let ips = config.hostEnv.ips.main;
in [ips.ip4] ++ (ips.ip6 or []);
fallbackVhost = {
certName = "quatresaisons";
hosts = [ "quatresaisons.immae.eu" ];
root = pkgs.runCommand "empty" {} "mkdir $out && touch $out/index.html";
extraConfig = [ "DirectoryIndex index.html" ];
};
vhostConfs.salle-s = {
certName = "quatresaisons";
addToCerts = true;
hosts = [ "salle-s.org" ];
root = toLanding ./quatresaisons/landing.yml;
extraConfig = [
''
<Directory ${toLanding ./quatresaisons/landing.yml}>
AllowOverride None
Require all granted
DirectoryIndex index.html
</Directory>
''
];
};
vhostConfs.tools = {
certName = "quatresaisons";
addToCerts = true;
hosts = [ "4c.salle-s.org" "quatresaisons.salle-s.org" "quatre-saisons.salle-s.org" ];
root = toLanding ./quatresaisons/landing_4c.yml;
extraConfig = [
''
Alias /charte ${serverSpecificConfig.charte_path}
<Directory ${serverSpecificConfig.charte_path}>
AllowOverride None
Require all granted
DirectoryIndex index.html index.txt
</Directory>
<Directory ${toLanding ./quatresaisons/landing_4c.yml}>
AllowOverride None
Require all granted
DirectoryIndex index.html
</Directory>
''
];
};
};
system.activationScripts.httpd = ''
install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php
install -d -m 0750 -o wwwrun -g wwwrun /var/lib/php/sessions
'';
services.phpfpm = {
phpOptions = ''
session.save_path = "/var/lib/php/sessions"
post_max_size = 20M
; 15 days (seconds)
session.gc_maxlifetime = 1296000
; 30 days (minutes)
session.cache_expire = 43200
'';
settings = {
log_level = "notice";
};
};
}