265 lines
12 KiB
Markdown
265 lines
12 KiB
Markdown
PAM et pam-auth-update
|
||
----------------------
|
||
|
||
L'authentification par mot de passe est gérée par le profil `common-auth`.
|
||
Ce profil n'est pas utilisé directement, il est inclus depuis les profils
|
||
de service présents dans `/etc/pam.d`.
|
||
|
||
Le contenu de ce profil commun est généré automatiquement par la commande
|
||
`pam-auth-update` pour tous les modules PAM qui livrent une définition dans
|
||
`/usr/share/pam-configs`. Cette commande peut être invoquée en mode interactif
|
||
(menu curses) ou en ligne de commande, éventuellement depuis un script de
|
||
paquet `postinst`. Voir [pam-auth-update(8)] pour les détails.
|
||
|
||
Les fichiers de définition dans `/usr/share/pam-configs` contiennent des
|
||
fragments de profil PAM, et aussi des méta-données qui sont utilisées
|
||
pendant l'exécution de `pam-auth-update`. La propriété la plus intéressante
|
||
est `Priority`: elle détermine l'ordre d'apparition du module dans la
|
||
liste du menu curses et dans le fichier `common-auth`. Les modules sont
|
||
classés par ordre de priorité décroissante. Voir [PAMConfigFrameworkSpec].
|
||
|
||
Extrait d'un `common-auth` généré avec les définitions `krb5`, `unix` et `ccreds`:
|
||
```
|
||
# here are the per-package modules (the "Primary" block)
|
||
auth [success=4 default=ignore] pam_krb5.so minimum_uid=1000
|
||
auth [success=3 default=ignore] pam_unix.so nullok_secure try_first_pass
|
||
auth [success=2 default=ignore] pam_ccreds.so minimum_uid=1000 action=validate use_first_pass
|
||
auth [default=ignore] pam_ccreds.so minimum_uid=1000 action=update
|
||
# here's the fallback if no module succeeds
|
||
auth requisite pam_deny.so
|
||
# prime the stack with a positive return value if there isn't one already;
|
||
# this avoids us returning an error just because nothing sets a success code
|
||
# since the modules above will each just jump around
|
||
auth required pam_permit.so
|
||
```
|
||
|
||
Les modules sont testés dans l'ordre d'apparition: d'abord `krb5`, si
|
||
l'authentification Kerberos échoue on passe à `unix`, si l'authentification
|
||
avec mot de passe local échoue on passe à `ccreds`, etc.
|
||
|
||
Important: l'option `success=N` est une instruction de saut: si le module concerné
|
||
authentifie l'utilisateur, alors les N modules suivants seront sautés, ce n'est pas
|
||
la peine de les invoquer. Autrement dit, si `pam_krb5` réussit, on passe directement
|
||
à `pam_permit`. Voir [pam.conf(5)] pour les détails sur les options.
|
||
|
||
|
||
Mise en place de `pam_sss`
|
||
--------------------------
|
||
|
||
Après avoir configuré SSSD, on active le profil `sss` avec `pam-auth-update`:
|
||
```
|
||
# pam-auth-update --remove krb5 --enable sss
|
||
```
|
||
|
||
Et là, c'est le drame : à l'ouverture de session, l'utilisateur n'obtient pas
|
||
de ticket Kerberos :-( alors que la machine est bien connectée au réseau.
|
||
Dans les logs, on a:
|
||
```
|
||
pam_unix(gdm-password:session): session opened for user meyd7400 by (uid=0)
|
||
```
|
||
C'est le module `unix` qui a authentifié l'utilisateur.
|
||
|
||
Sur une autre machine, où (par chance) le mot de passe local est différent
|
||
du mot de passe AD, le comportement est différent, le TGT est bien obtenu
|
||
à l'ouverture de session en utilisant le mot de passe AD.
|
||
Dans les logs, on a maintenant:
|
||
```
|
||
pam_unix(gdm-password:auth): authentication failure; logname= uid=0 euid=0 tty=/dev/tty1 ruser= rhost= user=meyd7400
|
||
pam_sss(gdm-password:auth): authentication success; logname= uid=0 euid=0 tty=/dev/tty1 ruser= rhost= user=meyd7400
|
||
[...]
|
||
pam_unix(gdm-password:session): session opened for user meyd7400 by (uid=0)
|
||
```
|
||
Le module `unix` a échoué (normal) et le module `sss` a réussit.
|
||
|
||
Pour comprendre ce comportement, il faut regarder dans le profil `common-auth`,
|
||
qui contient maintenant ceci:
|
||
|
||
```
|
||
# here are the per-package modules (the "Primary" block)
|
||
auth [success=4 default=ignore] pam_unix.so nullok_secure
|
||
auth [success=3 default=ignore] pam_sss.so use_first_pass
|
||
auth [success=2 default=ignore] pam_ccreds.so minimum_uid=1000 action=validate use_first_pass
|
||
auth [default=ignore] pam_ccreds.so minimum_uid=1000 action=update
|
||
```
|
||
|
||
Le module `unix` est placé en premier, donc il réussit toujours quand le
|
||
mot de passe local est synchronisé avec le mot de passe AD, et alors le
|
||
module `sss` n'est pas appelé à cause de l'option `success`, cf plus haut.
|
||
|
||
Si le module `sss` était placé en premier, comme l'était `krb5`,
|
||
alors on aurait eu le résultat attendu. Mais il est placé en second par
|
||
`pam-auth-update`, parce que sa priorité (128) est inférieure à celle de
|
||
`unix` (256).
|
||
|
||
|
||
Le module `pam_sss` en premier
|
||
------------------------------
|
||
|
||
D'apres [PAMConfigFrameworkSpec], le module `pam_sss` devrait avoir une
|
||
priorité supérieure ou égale à 512, puisqu'il s'agit d'un profil
|
||
qui "authenticates against a central network service, with rules to
|
||
exclude administrative/system accounts". Or sa priorité définie dans
|
||
`/usr/share/pam-configs/sss` est 128 ... pourquoi ?
|
||
|
||
Une réponse possible: le module `pam_sss` ne prend pas en compte l'option
|
||
PAM classique `minimum_uid` comme le font `pam_krb5` et `pam_ccreds`, il
|
||
utilise pour faire la même chose une propriéte `min_id` à définir dans
|
||
le fichier de configuration `sssd.conf`. Comme la règle d'exclusion n'est
|
||
pas explicitement dans la configuration PAM, ca pourrait expliquer le
|
||
choix d'une priorité inférieure.
|
||
|
||
=> chercher dans le bugtracker ?
|
||
|
||
On dirait vraiment que `pam_sss` est conçu et configuré pour des
|
||
utilisateurs qui sont enregistrés dans un LDAP et pas en local (dans ce
|
||
cas ça marche direct). Moi je veux bien, c'est parfait pour un poste
|
||
fixe qui est toujours connecté au réseau, mais que se passe-t'il avec
|
||
un laptop déconnecté, si on n'a pas de mot de passe local ?
|
||
|
||
On pourrait envisager de désactiver le profil `pam_unix`, comme cela il
|
||
ne reste que `pam_sss` et le problème disparait, mais je soupçonne qu'il
|
||
va y avoir des effets de bord ;-)
|
||
|
||
Pour passer `pam_sss` en premier, il faut augmenter sa priorité. On peut
|
||
faire cela en modifiant le fichier `/usr/share/pam-configs/sss` à la
|
||
volée depuis le `postinst` avec `sed`, mais c'est très moche, et cela ne
|
||
résiste pas à une mise à jour du paquet `libpam-sss`, il faudrait mettre
|
||
en place un `trigger` ou mieux encore un `divert` ... ça reste du bricolage.
|
||
|
||
Autre solution, plus propre:
|
||
- ajouter un fichier `/usr/share/pam-configs/yourdev`, copie de `sss`
|
||
- modifier sa propriété `Name` et augmenter la valeur de `Priority`
|
||
- rajouter une propriété `Conflicts: sss`
|
||
- dans le `postinst`, activer `yourdev` et désactiver `sss`
|
||
|
||
Important: ne pas utiliser --force dans les scripts, seulement --package
|
||
Important: bien verifier uid >= 1000 et users interdits.
|
||
|
||
Note: les deux profils `sss` et `yourdev` sont en conflit, mais leur
|
||
propriété `Default` vaut `yes`, ce qui a une conséquence particulière
|
||
: quand on lance `pam-auth-update` une première fois en mode interactif,
|
||
sans avoir exécuté la configuration automatique via le `postinst`, alors
|
||
les deux profils sont sélectionnés dans le dialogue `debconf`. Si on
|
||
valide à ce moment, le dialogue se plaint qu'il y a conflit, et refuse de
|
||
valider la sélection. Il faut modifier et choisir l'un ou l'autre. Ensuite,
|
||
le dialogue se souvient du choix qui a été fait.
|
||
|
||
Avec une définition `sss-first` dans `pam-configs`, le fichier `common-auth`
|
||
devient comme prévu:
|
||
```
|
||
# here are the per-package modules (the "Primary" block)
|
||
auth [success=4 default=ignore] pam_sss.so forward_pass
|
||
auth [success=3 default=ignore] pam_unix.so nullok_secure try_first_pass
|
||
auth [success=2 default=ignore] pam_ccreds.so minimum_uid=1000 action=validate use_first_pass
|
||
auth [default=ignore] pam_ccreds.so minimum_uid=1000 action=update
|
||
# here's the fallback if no module succeeds
|
||
auth requisite pam_deny.so
|
||
```
|
||
|
||
|
||
Changement de mot de passe
|
||
--------------------------
|
||
|
||
Avec le module `pam_sss` en premier, on a le comportement attendu, c'est
|
||
SSSD qui fait l'authentification et fournit un TGT Kerberos à l'ouverture
|
||
de session. Mais c'est aussi SSSD qui gère le changement de mot de passe,
|
||
avec des conséquences imprévues:
|
||
|
||
- c'est le mot de passe réseau qui est changé via la commande `passwd`,
|
||
pas le mot de passe local, et celui-ci est pénible à modifier, voir
|
||
plus bas.
|
||
|
||
- sur le réseau, le mot de passe AD est changé, mais pas le mot de passe
|
||
du Gassi, les deux bases ne sont pas synchronisées, changer l'une ne
|
||
modifie pas l'autre, le nouveau mot de passe sera requis pour Kerberos,
|
||
IMAP, SMTP, SMB, etc. mais ne fonctionnera pas pour tous les sites en
|
||
SSO derrière le Gassi qui utilise toujours l'ancien ...
|
||
|
||
Commandes pour modifier le mot de passe local alors que SSSD est utilisé:
|
||
```
|
||
$ sudo pam-auth-update --remove yourdev
|
||
$ passwd
|
||
$ sudo pam-auth-update --enable yourdev
|
||
```
|
||
|
||
Tout ceci est très pénible... une méthode simple de résoudre le problème
|
||
serait d'empêcher la modification du mot de passe réseau depuis l'hôte,
|
||
puisque de toute façon cela ne fonctionne pas complètement (cf la
|
||
synchronisation AD/Gassi).
|
||
|
||
Pour désactiver la fonctionnalité "changement de mot de passe" pour le
|
||
module `pam_sss` depuis `pam-configs`, il suffit d'éditer la configuration,
|
||
puis supprimer toute la section concernée (propriétés `Password-Type`,
|
||
`Password` et `Password-Initial`), et relancer la commande `pam-auth-update`.
|
||
|
||
La section à supprimer dans `/usr/share/pam-configs/yourdev`:
|
||
```
|
||
Password-Type: Primary
|
||
Password:
|
||
sufficient pam_sss.so use_authtok
|
||
Password-Initial:
|
||
sufficient pam_sss.so
|
||
```
|
||
|
||
Après génération, on constate qu'il n'y a plus aucune mention du module
|
||
`pam_sss` dans le profil `/etc/pam.d/common-passwd`.
|
||
|
||
A partir de là, pour changer le mot de passe local, il suffit d'utiliser
|
||
la commande classique `passwd` comme ceci:
|
||
```
|
||
$ passwd
|
||
Changement du mot de passe pour pipo1234.
|
||
Mot de passe actuel :
|
||
Nouveau mot de passe :
|
||
Retapez le nouveau mot de passe :
|
||
passwd : le mot de passe a été mis à jour avec succès
|
||
```
|
||
Le "mot de passe actuel" est bien entendu le mot de passe _local_ courant,
|
||
puisque la commande ne pourra invoquer que le module `pam_unix`.
|
||
|
||
Il est toujours possible de changer le mot de passe réseau avec la
|
||
commande `kpasswd` du paquet `krb5-user` qui discute en direct avec le
|
||
serveur Kerberos, sans passer par la configuration PAM ou le démon SSSD,
|
||
mais cela n'est pas conseillé.
|
||
|
||
Pour plus de "sécurité", afin d'éviter les fausses manoeuvres et
|
||
une desynchronisation accidentelle des mots de passes AD et Gassi, on
|
||
peut désactiver le changement de mot de passe par SSSD en rajoutant la
|
||
propriété `chpass_provider = none` pour chaque domaine dans `sssd.conf`.
|
||
Une fois ce réglage en place, les tentatives de modification du mot
|
||
de passe réseau par SSSD vont échouer systématiquement avec une
|
||
"erreur système" lors de l'authentification préalable.
|
||
|
||
On conseille de synchroniser le mot de passe local avec le mot de passe
|
||
réseau, pour éviter les surprises lorsque le serveur d'authentification
|
||
n'est pas joignable.
|
||
Pour changer le mot de passe, la procédure se décompose en deux étapes:
|
||
|
||
1. changer le mot de passe réseau sur [Mot de passe attitude]
|
||
2. changer le mot de passe local avec `passwd`
|
||
|
||
|
||
Reste à faire
|
||
-------------
|
||
|
||
Il doit bien y avoir une raison explicite pour que `pam_sss`
|
||
ne soit pas prioritaire dans la configuration par défaut Debian/Ubuntu,
|
||
la "réponse possible" ci-dessus n'est qu'une supposition de ma part, tant
|
||
que je n'aurais pas trouvé l'explication dans une documenation officielle
|
||
il restera un doute sur la méthode ci-dessus.
|
||
|
||
|
||
Références
|
||
----------
|
||
|
||
- [pam.conf(5)]
|
||
- [pam-auth-update(8)]
|
||
- [PAMConfigFrameworkSpec]
|
||
|
||
[PAMConfigFrameworkSpec]: https://wiki.ubuntu.com/PAMConfigFrameworkSpec
|
||
[pam.conf(5)]: https://manpages.debian.org/bullseye/libpam-runtime/pam.conf.5.en.html
|
||
[pam-auth-update(8)]: https://manpages.debian.org/bullseye/libpam-runtime/pam-auth-update.8.en.html
|
||
[Mot de passe attitude]: https://mpa.itn.ftgroup/QPM/User/
|
||
|
||
|