Résumé : sur un seul serveur GNU/Linux, comment surcharger l'emplacement du dossier personnel ($HOME) récupéré dans un annuaire LDAP ? Il ne faut pas ré-écrire la valeur de la variable $HOME avec PAM, car cela causera des dysfonctionnements des logiciels / commandes qui récupèrent l'emplacement du dossier personnel avec la fonction C getpwuid()
qui utilise NSS. Il faut jouer avec NSS / SSSD / NSLCD. Néanmoins, si les dossiers personnels sont situés dans plusieurs arborescences en fonction de l'identifiant de l'utilisateur, alors je n'ai pas trouvé de solution. Encore une fois, toute cette aventure permet de se remémorer les bases d'un système GNU/Linux.
D'une manière générale, deux espaces de stockage sont à la disposition de nos utilisateurs : leur dossier personnel ($HOME) et leurs données.
Nous avons également un serveur Debian GNU/Linux sur lequel tous nos utilisateurs peuvent se connecter en SSH et déposer des pages web (entre autres choses). Authentification LDAP. Sur ce serveur, c'est l'espace de stockage pour les données qui est monté en tant que dossier personnel, pas l'espace personnel. Pourquoi ?
Sur notre ancien NAS, les données étaient rangées de telle façon que le chemin est identique entre les données et le dossier personnel, seul le volume / partage change. Du coup, c'est bien les données, et non pas les homedirs, qui sont montées aux emplacements désignés par l'attribut LDAP « homeDirectory ».
Sur notre nouveau NAS, ce n'est pas le cas, car nous sommes limités par la grammaire des règles de tri automatiques (pour des raisons de performances) de notre nouveau NAS. Le dossier « données » d'un utilisateur est en fait un sous-dossier d'un dossier qui peut prendre le nom d'un hash ou celui du groupe auquel appartient l'utilisateur (son service, par exemple) ou l'année d'arrivée de l'utilisateur, etc. Les données ne sont pas toutes au même endroit. Les dossiers personnels le sont. Il n'y a plus équivalence entre le chemin du dossier personnel et le chemin des données. Il n'y a même pas le niveau hiérarchique (pas le même nombre de sous-dossiers depuis la racine) entre le homedir de deux utilisateurs.
Le chemin est prédictible à partir de l'identifiant de l'utilisateur.
Des liens symboliques ne sont pas la solution car tous les homedirs sont dans un même dossiers et les données dans X dossiers. Il faudrait donc autant de liens que d'utilisateurs, et faire vivre cette forêt de liens en tenant compte des arrivées et des départs des utilisateurs.
Comment faire pour continuer à monter les données personnelles à la place du dossier personnel avec notre nouveau NAS ?
La première idée est d'utiliser PAM afin qu'il surcharge la variable $HOME. Une recherche sur le web indique que cela se fait en modifiant le fichier /etc/security/pam_env.conf
. La doc' est ici.
Vu qu'il s'agit d'un fichier plat et non d'un script, ça ne répond pas au besoin par absence de gestion des différents emplacements du dossier personnel en fonction de l'identifiant.
De plus, pour que ça fonctionne, il faut faire un disgracieux cd ~
dans /etc/profile
.
On notera également que cette méthode ne bernera pas les environnements de bureau : les applications lancées par celui-ci risquent de ne pas avoir le bon $HOME. Dans mon cas, le serveur est uniquement accessible en SSH, donc ce point n'est pas limitant.
pam-script est un module pour PAM qui permet de lancer des scripts aux différentes étapes de PAM (authentification, ouverture de session, fermeture de session, etc.). On pourrait s'en servir pour surcharger la variable $HOME ?
Après test, je peux affirmer que non. Les variables définies / surchargées dans un script pam-script ne sont pas conservées. Même si l'on s'assure, via /etc/pam.d/common-auth
, de lancer pam-script en dernier afin de s'assurer qu'un autre module PAM n'écrase pas la variable.
PAM propose la fonction pam_putenv(). Écrivons un module PAM qui utilise cette fonction afin de surcharger la variable $HOME.
Mes dérives par rapport au tutoriel :
pam_putenv(pamh, "HOME=/home/testpam/bidule");
;libpam0g-dev
donc il faut qu'il soit installé ;gcc -fPIC -DPIC -shared -rdynamic -o pam_ignore.so pam_ignore.c
;/lib/security/pam_ignore.so
;session required pam_ignore.so
à la fin du fichier /etc/pam.d/common-session
. Il faut ajouter à la fin afin de s'assurer qu'un autre module n'écrase pas la variable $HOME.Pour tester, j'utilise su -
et su - <utilisateur_que_je_crée_pour l'occasion>
en ayant créé le dossier /home/testpam/bidule
au préalable :
$ su - testpam
Mot de passe :
$ pwd
/home/testpam
$ echo $HOME
/home/testpam/bidule
Cela fonctionne, mais il faut à nouveau user d'un disgracieux cd ~
dans /etc/profile
…
Même sans ça, je n'adopterai pas cette solution. Avec PAM, on peut se faire très mal niveau sécurité. Il faut vraiment savoir ce que l'on fait et tester tous les cas d'usage. Nous avons un petit système d'information, rien justifie une telle bidouille et sa complexité (écrire un module PAM pour un problème si banal, allô !). Il doit exister une réponse plus simple à notre problème.
Pourquoi faut-il ajouter cd ~
dans le fichier /etc/profile
pour que le shell se positionne dans le dossier que l'on définit dans $HOME ?
Quelque chose instancie notre shell dans le « vrai » dossier personnel sans tenir compte de notre falsification.
Pour tenter de l'identifier, j'ajoute le bout de code suivant dans la fonction « pam_sm_open_session() » de mon module PAM (il affiche un message + le dossier courant de travail) :
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("Coucou depuis PAM : %s\n", cwd);
}
else {
perror("getcwd() error");
return 1;
}
Dans /etc/passwd
, je change le home directory de mon utilisateur de test pour un emplacement qui n'existe pas, « /home/testpamFAIL ».
$ su - testpam
Mot de passe :
Coucou depuis PAM : /home/guigui
su: warning: cannot change directory to /home/testpamFAIL: Aucun fichier ou dossier de ce type
Le déplacement dans le home directory se fait après l'exécution de PAM. Comment ? su -
doit probablement interroger NSS qui, lui, est configuré (/etc/nsswitch.conf
) pour consulter /etc/passwd
ou LDAP ou… Comment s'en assurer ?
$ whereis su
su: /bin/su /usr/share/man/man1/su.1.gz
$ strings /bin/su | grep -E '(getpwuid|chdir)'
getpwuid
chdir
La fonction de la libc getpwuid() récupère les infos d'un utilisateur via les bases de données (LDAP, /etc/passwd, etc.) configurées dans NSS.
La fonction chdir() change le dossier de travail d'un processus.
Ces deux fonctions sont mentionnées dans le code de su
. Ça ne prouve pas qu'elles sont appelées, mais avec le message ci-dessus, ça donne un sacré indice.
Et avec SSH, il se passe quoi ?
$ ssh testpam@::1
testpam@::1's password:
Could not chdir to home directory /home/testpamFAIL: No such file or directory
Coucou depuis PAM : /
Last login: Tue Dec 29 22:16:46 2020 from ::1
$
Le déplacement dans le dossier personnel se fait avant l'invocation de PAM. Un home directory positionné à « / » est caractéristique du comportement de sshd
en l'absence d'un homedir.
$ whereis sshd
sshd: /usr/sbin/sshd /usr/share/man/man8/sshd.8.
$ strings /usr/sbin/sshd | grep -E '(getpwuid|chdir)'
getpwuid
chdir
[…]
Could not chdir to home directory %s: %s
[…]
Avec ce message d'erreur mentionné dans le binaire, le doute n'est plus possible : c'est bien sshd
qui récupère le home directory via NSS et fait apparaître un shell dans cet emplacement.
Cela signifie qu'il est vain de modifier la valeur de la variable $HOME depuis PAM.
Il est tout aussi vain de chdir()
depuis PAM : ce n'est pas lui qui lance le shell, donc ça aura aucun effet.
Tous les logiciels qui utilisent la fonction getpwuid()
ne seront pas dupés par une surcharge de la variable $HOME. Genre ssh
pour récupérer sa configuration dans ~/.ssh/config, vim
pour choper .vimrc
, etc. Cela entraînera des dysfonctionnements. Or, il y a un petit paquet de logiciels courants en ligne de commande qui utilisent getpwuid()
:
$ for file in /usr/bin/*
> do
> strings $file | grep -q getpwuid && echo $file;
> done | wc -l
314
Conclusion : si l'on veut trafiquer / surcharger / faker l'emplacement du dossier personnel, il vaut mieux agir côté NSS.
Avec libnss-ldap (prise en charge de LDAP par NSS), on peut utiliser /etc/libnss-ldap.conf
(ou /etc/ldap.conf) pour substituer un attribut LDAP à un autre (« nss_map_attribute ») ou ré-écrire / surcharger la valeur d'un attribut LDAP (« nss_override_attribute_value »). Documentation.
On peut faire pareil avec SSSD (démon pour une authentification centralisée + mise en cache via un framework commun) : « override_homedir ». Les jokers (%u = identifiant, %d = domaine, etc.) autorisés sont précisés dans la doc'.
Idem avec nslcd (démon qui s'interface entre NSS et un serveur LDAP) : « map MAP ATTRIBUTE NEWATTRIBUTE ». Les jokers autorisés (plus nombreux que ceux de sssd) sont également précisés dans la documentation.
Toutes ces solutions ont le même défaut : elles autorisent quelques substitutions (jokers), mais ça ne permet pas de répondre à notre besoin d'une fluctuation de l'arborescence de stockage des homedirs en fonction de l'identifiant.
On pourrait remplacer (mapper) l'attribut LDAP « homeDirectory » par un autre attribut LDAP, mais il faudrait modifier notre nuée de scripts d'importation des comptes utilisateur depuis le logiciel RH. Tout ça pour un seul serveur… Démesuré + long à entreprendre + pénible à maintenir (une exception de plus…).
Au final, sur ce serveur, nous avons monté les dossiers personnels en tant que homedirs (nous ne montons plus les données personnelles à la place du homedir, quoi). Cela fait 6 mois que c'est en place et personne nous a signalé un conflit entre des fichiers de conf' Ubuntu et Debian.
J'ai découvert la grande flexibilité du module userdir d'Apache httpd : le site web d'un utilisateur n'est pas obligatoire situé dans son dossiers personnel. Ainsi, les sites web continuent à être stockées dans les données et nos utilisateurs peuvent y accéder depuis n'importe où en CIFS.
Un script lancé par pam-script crée un lien symbolique vers les données dans le dossier personnel et la doc' utilisateur a été mise à jour afin que les utilisateurs ne soient pas perdus.
Mon script pam-script crée aussi le dossier personnel s'il n'existe pas. Je n'utilise pas le module PAM pam_mkhomedir car il n'applique pas le umask qu'on lui donne en paramètre. De plus, PAM est exécuté avec les droits root, donc il tente de créer le homedir avec ces droits-là, et se fait refuser l'accès par le NAS (seul un utilisateur peut écrire). Mon script exécute un sudo -u "$PAM_USER" mkdir "$HOMEDIR"
.
Je retiens qu'il faut être prudent quand on joue avec pam-script car on peut ouvrir un serveur à tous les vents par mégarde et que la configuration par défaut empêche de changer un mot de passe (même root).
Merci Johndescs pour le coup de papatte. :)