Pour déléguer des actions à des « power users », vous utilisez probablement la commande sudo. Mais votre confiance est limitée et vous souhaiteriez avoir une trace des actions effectuées : nous allons voir dans cet article différentes solutions pour y parvenir. En particulier, nous allons découvrir le mécanisme de plug-ins (que l’on peut écrire en Python) qui permet d’enrichir sudo sans restriction !
1. Introduction
Vous connaissez tous l’intérêt de sudo [1] et vous avez pris l’habitude de configurer cet utilitaire à l’aide de la commande visudo qui permet d’éditer, sans erreur de syntaxe, le fichier /etc/sudoers.
Mais connaissez-vous toutes les options que l’on peut y définir ? Êtes-vous sûr que vous tracez toutes les commandes exécutées par les utilisateurs de sudo ?
Nous allons vous aider à y voir plus clair parmi les solutions possibles. Le choix final restera bien sûr entre vos mains !
2. Intégration de sudo et rsyslog
Par défaut, les utilisations de sudo génèrent des logs qui sont émis à syslog (rsyslogd) avec la catégorie (traduction libre de l’anglais facility) authpriv. Sur le serveur suivant (un CentOS 9), les messages de cette catégorie sont écrits dans le fichier /var/log/secure, ce que nous pouvons vérifier :
Le manuel sudoers(5) nous indique les options du fichier /etc/sudoers qui permettent de modifier ce comportement par défaut. Par exemple, l’option syslog permet de choisir une autre catégorie :
Mais sudo peut aussi écrire ses logs dans son propre fichier de logs grâce à l’option logfile :
Vérifions cela :
Le fichier de logs a bien été créé et dûment rempli par sudo.
3. Génération de logs avec sudo
Mais que se passe-t-il si l’utilisateur lance la commande su ou bien ouvre un nouveau shell à partir de sudo ? Essayons et regardons ce qui est enregistré dans les logs :
Les logs ne montrent pas les commandes exécutées à partir de la nouvelle session shell :
On ne voit pas de log concernant la commande « ls /tmp ».
Heureusement, les options log_input et log_output permettent de résoudre ce problème. En activant ces options, sudo va enregistrer les sessions dans le répertoire /var/log/sudo-io (répertoire configurable via l’option iolog-dir). Ajoutons ces options au fichier /etc/sudoers :
Puis testons :
Notez que dorénavant, les logs « normaux » affichent une valeur supplémentaire : un TSID qui est un identifiant de session :
La session a bien été enregistrée quelque part dans le répertoire /var/log/sudo-io :
Les commandes exécutées par l’utilisateur jd peuvent être affichées ainsi :
C’est bien cela : on retrouve notre « ls /tmp » ! Comme vous le voyez, les informations sont stockées en format compressé et sont plutôt destinées à la commande sudoreplay qui permet de rejouer des sessions enregistrées.
Les options log_input et log_output peuvent être surchargées commande par commande avec les tags LOG_INPUT, NOLOG_INPUT, LOG_OUTPUT et NOLOG_OUTPUT. En effet, dans certains cas, l’option log_input peut empêcher un script shell qui lit sur STDIN de fonctionner.
La possibilité d’enregistrer les sous-commandes lancées à partir du shell bash de notre exemple est aussi possible simplement en activant l’option suivante dans le fichier /etc/sudoers :
Réessayons la manipulation précédente :
Et maintenant, regardons les logs :
La commande « cat /etc/motd » est bien loggée aussi !
4. Intégration de sudo et PAM & tlog
4.1 Mise en œuvre de pam_tty_audit
Si notre objectif est d’enregistrer toutes les touches tapées au clavier par les utilisateurs de sudo, il existe une solution générale qui utilise le module pam_tty_audit.so. Comme chacun le sait, sudo est « compatible PAM ». Vous pouvez le vérifier à l’aide de la commande suivante (exécutée sur une distribution Rocky 8) :
La configuration de PAM pour sudo est consignée dans le fichier /etc/pam.d/sudo qui ressemble à ceci :
Notez que le fichier /etc/pam.d/sudo-i qui est utilisé par PAM lorsque l’utilisateur invoque la commande « sudo -i » inclut le fichier /etc/pam.d/sudo. Nous pouvons donc ignorer ce fichier ici.
La documentation du module pam_tty_audit indique que ce module ne peut s’appliquer qu’à la rubrique session, ce qui fait sens puisque nous voulons auditer les entrées au clavier après l’ouverture de la session. Nous ajoutons une ligne dans le fichier /etc/pam.d/sudo qui devient celui-ci :
Le paramètre enable=* indique que nous activons l’audit pour tous les utilisateurs, y compris root.
Maintenant, ouvrons une nouvelle session utilisateur via sudo et exécutons quelques commandes :
Les frappes au clavier sont stockées dans le fichier /var/log/audit/audit.log, périodiquement ou bien quand la session se termine, mais pas en temps réel.
Comme on le voit avec la commande suivante, ce sont les valeurs ASCII des touches qui sont stockées :
Ce n’est pas très lisible, heureusement, la commande aureport vient à notre secours :
4.2 Mise en œuvre de tlog
Disponible à partir de la RHEL/CentOS 8, tlog est une solution alternative à pam_tty_audit. Après avoir installé ce package, il faut configurer l’outil d’enregistrement des sessions nommé tlog-rec-session. Cette configuration est facilement réalisable à partir de l’interface web de cockpit (si vous installez aussi le package cockpit-session-recording), ou bien directement avec un éditeur de texte. Le but est de renseigner le contenu du fichier /etc/tlog/tlog-rec-session.conf :
Deux remarques : nous pouvons choisir le message qui indiquera clairement aux utilisateurs que leur session est enregistrée et nous pouvons choisir le writer, ici, ce sera le fichier /var/log/tlog/users.log. Nous avons donc créé le répertoire /var/log/tlog et avons attribué son propriétaire à tlog:tlog.
Il reste à configurer le démon sssd, invoqué via le module pam_sssd. Le fichier /etc/sssd/conf.d/session-recording.conf contient ces directives :
Redémarrons le service sssd, puis testons en prenant l’identité de l’utilisateur jd :
Toutes les actions sont enregistrées dans le fichier /var/log/tlog/users.log :
5. Mise en œuvre du serveur sudo_logsrvd
Résumons ce que nous avons réalisé jusqu’ici :
- toutes les commandes exécutées via sudo peuvent être envoyées à (r)syslog ;
- en cas d’ouverture de session via sudo, toutes les frappes au clavier sont envoyées à auditd grâce au module pam_tty_audit ;
- ou bien nous pouvons mettre en œuvre l’alternative proposée par tlog.
Pouvons-nous encore renforcer la robustesse de notre système en nous prémunissant, par exemple, contre un utilisateur qui, devenu « root », tenterait d’effacer des logs ?
À partir de la version 1.9.0, un serveur de logs dédié à sudo est disponible. Il s’agit de sudo_logsrvd ! Ce serveur reçoit des messages de la part de sudo et peut les stocker localement aussi bien que les relayer vers un autre serveur (un syslog ou un autre sudo_logsrvd). Le serveur est facile à déployer, encore faut-il qu’il soit fourni avec votre distribution ! Sur notre serveur (une CentOS 9), c’est bien le cas, cependant la version proposée (une 1.9.5.p2) est buggée. Nous devons donc recompiler sudo :
La nouvelle version de sudo est dorénavant dans /usr/local/bin.
Pour tester sudo_logsrvd localement et rapidement, nous allons le lancer en avant-plan avec un fichier de configuration minimaliste pour qu’il écoute sur le port 30343 et stocke ses événements dans le répertoire /var/log/sudo-logsrvd :
Il faut maintenant configurer sudo pour qu’il envoie les événements au serveur. À l’aide de visudo, nous ajoutons la ligne suivante au fichier /etc/sudoers :
Et nous testons :
Les sessions enregistrées peuvent être affichées – voire rejouées – par la commande sudoreplay :
Si vous arrêtez le serveur sudo_logsrvd, il devient impossible d’utiliser sudo :
Les paramètres ignore_iolog_errors ou ignore_log_errors permettent d’éviter ce comportement. Il est aussi possible de spécifier plusieurs serveurs de logs pour assurer une forme de haute disponibilité.
6. Écrire son propre plug-in pour sudo
Et si maintenant nous voulions implémenter une idée originale qui n’est pas supportée par les solutions précédentes ?
6.1 Architecture de sudo
Ici, un petit élément d’architecture de sudo s’impose ! Si vous utilisez une version supérieure ou égale à la v1.8 et que vous avez déjà configuré sudo, vous avez probablement uniquement utilisé la commande visudo pour modifier le fichier /etc/sudoers ? Mais vous ne vous êtes probablement pas rendu compte qu’il existait un fichier nommé /etc/sudo.conf ?
Dans ce fichier de configuration, trois directives nous intéressent ici :
Que signifient-elles ?
- que sudo est modulaire et utilise des plug-ins !
- qu’il existe des plug-ins pour implémenter la politique d’accès aux commandes privilégiées, des plug-ins pour gérer les entrées/sorties des commandes exécutées et des plug-ins d’audit qui enregistrent les utilisations fructueuses et erronées de sudo ;
- le plug-in par défaut qui s’appelle sudoers.so, possède trois rôles (policy, io et audit), et implémente la politique définie dans le fichier /etc/sudoers.
Pour information, les plug-ins sont situés dans le répertoire /usr/libexec/sudo/.
La documentation officielle de sudo nous indique qu’il existe d’autres catégories de plug-ins :
- les plug-ins d’approbation qui sont appelés après le plug-in d’application de la politique. Ceux-ci peuvent alors refuser l’action demandée sur la base de contraintes supplémentaires ;
- les plug-ins de gestion de groupe qui permettent d’appliquer des relations entre utilisateurs et groupes autres que celles définies au niveau du système ou dans le fichier /etc/sudoers lui-même.
La documentation nous apprend aussi qu’à partir de la version 1.9 de sudo, il est possible d’écrire ses propres plug-ins en Python ! Pour connaître votre version de sudo, rappelez-vous que la commande « sudo -V » affiche la version (voir §4).
Nous allons donc écrire un plug-in de gestion des E/S en Python, ainsi nous pourrons aussi capturer les commandes qu’un utilisateur invoquerait à partir d’un shell ouvert par sudo !
6.2 Squelette de notre plug-in
Notre plug-in est écrit sous forme d’une classe qui doit dériver de sudo.Plugin. La méthode __init__ est optionnelle, mais elle nous permet de recevoir d’éventuels paramètres de configuration. La méthode open est appelée avant d’exécuter la commande de l’utilisateur et la méthode close est appelée à la fin de l’exécution de la commande. Pour finir, les méthodes log_ttyin, log_ttyout permettent d’intercepter les entrées clavier et la sortie à l’écran.
sudo est livré avec des exemples de plug-ins écrits en Python (en général situés dans le répertoire /usr/share/doc/sudo/examples/).
Le squelette de notre plug-in, que nous nommerons user_history.py, s’écrit ainsi :
Pour intégrer le plug-in à sudo, il suffit de rajouter une ligne au fichier /etc/sudo.conf :
Comme vous le voyez, il faut indiquer le type du plug-in (python_io), sa localisation (ModulePath) et le nom de la classe qui le définit (ClassName).
Testons notre plug-in en exécutant une commande simple :
Nous voyons que nous avons bien intercepté la commande demandée (ici, c’était id).
Attention, petit piège ! Quand vous donnez le chemin complet de votre plug-in avec le paramètre ModulePath, dans le fichier /etc/sudo.conf, l’algorithme interne découpe le nom en répertoire + nom de fichier. Puis le nom du répertoire est ajouté à la fin des chemins de recherches utilisés pour les importations de Python. Ainsi, si votre plug-in s’appelle /chemin/vers/audit.py, il ne sera jamais correctement chargé, car il est fort probable que sudo importera un module audit.py situé dans un répertoire plus prioritaire ! Conclusion : prenez toujours des noms « métiers » (le comportement a été remonté au développeur actuel de sudo, Todd C. Miller, qui a ajouté une note sur son blog) !
Pour des raisons de sécurité évidentes, le plug-in doit être propriété de root et accessible en écriture uniquement par root !
6.3 Finalisation de notre plug-in
Nous savons intercepter les commandes exécutées par l’utilisateur de sudo et pour l’instant, nous avons reproduit à l’aide d’un code Python le comportement de l’option log_subcmds présentée au §3. Essayons d’étayer notre plug-in en lui ajoutant une fonctionnalité supplémentaire : nous voulons que les commandes exécutées à l’aide de sudo soient historisées dans un fichier du compte de l’utilisateur et non pas dans l’historique de « root ».
Nous allons donc ajouter des paramètres à notre plug-in :
- Histfile indiquera le nom du fichier d’historique (on gérera le caractère tilde qui symbolise le répertoire d’accueil de l’utilisateur) ;
- Prefix permettra d’ajouter un préfixe à la commande pour indiquer qu’elle a été lancée via sudo ;
- AsComment sera un booléen qui indiquera si la commande doit être précédée d’un caractère dièse (il est impossible que la valeur du Prefix contienne un #, car ce caractère indique dans /etc/sudo.conf le début d’un commentaire!) ;
- Verbose sera un booléen qui permettra d’activer le mode verbeux/debug.
L’activation du plug-in dans le fichier /etc/sudo.conf devient :
Pour prendre en compte les paramètres du plug-in, la méthode __init__ s’écrit maintenant :
La méthode _canonicalize a pour vocation de traiter le tilde dans les noms de fichiers :
La méthode open s’enrichit d’un appel à la méthode interne _log_history qui historise une commande en la préfixant par la valeur du paramètre Prefix :
Il reste à gérer les touches du clavier interceptées par la méthode log_ttyin et là, il va y avoir quand même un problème ! Quand l’utilisateur édite sa commande et utilise la touche <backspace> ou bien les flèches du curseur, la chaîne finale obtenue est incorrecte et ne reflète pas la véritable commande exécutée. Par exemple, si l’utilisateur tape les touches suivantes :
vous obtiendrez la chaîne :
… à moins de traiter les touches spéciales !
Le code suivant traite correctement le <backspace>, mais termine le shell lancé par sudo si une flèche de curseur a été tapée. Il ne tient qu’à vous d’enrichir ce code !
Pour finir, testons notre plug-in :
Vérifions le contenu de notre historique des commandes exécutées directement ou indirectement via sudo :
L’historique est bien complet et stocké dans un fichier propre à l’utilisateur !
Conclusion
Sans prétendre être exhaustifs, nous avons présenté diverses solutions, de la plus simple à la plus sophistiquée, qui permettent d’enregistrer les commandes exécutées par les utilisateurs, y compris quand ils emploient sudo. Concernant cet utilitaire, nous vous avons aussi montré qu’il était facile de l’enrichir en implémentant vos propres besoins par le biais de plug-ins écrits en Python ! Il ne reste plus qu’à faire preuve d’inventivité !
Références
[1] Site officiel de sudo : https://www.sudo.ws/
[2] Source du plug-in : https://github.com/majeinfo/sudo_plugin