Un chat Unix simple
(the-dam.org)sucest un Simple Unix Chat qui implémente une partie des fonctionnalités clés d’un chat de type Slack·Mattermost avec des briques UNIX de base combinées à Bash ; la boucle centrale tient en 5 lignes, même si le script réel est plus long- Les messages sont lus ligne par ligne depuis l’entrée standard, horodatés et associés à un nom d’utilisateur avant d’être ajoutés au fichier du canal dans
/var/lib/suc/, tandis que l’authentification et le contrôle d’accès sont délégués à SSH et au modèle de permissions UNIX - Alors que le serveur Mattermost représente à lui seul environ 500 000 lignes de code Go,
sucsouligne qu’un comportement central similaire peut être obtenu avec un code bien plus réduit usuccombinetail -f,rlwrap,pygmentizeet le préfixe de commande:pour gérer la lecture des canaux, l’affichage de style Markdown et le partage des résultats de commandes- Comme les canaux ne sont que de simples fichiers texte, ils s’intègrent facilement avec des outils UNIX comme
grep,tail,bat,lnav, des hooks Git ou des scripts de bot
La complexité que suc cherche à réduire
sucsignifie Simple Unix Chat ; il offre moins de fonctionnalités que Slack, Mattermost ou IRC, et dépasse bien 5 lignes au total, mais sa boucle centrale d’écriture de messages tient en 5 lignes de Bash- Les fonctionnalités visées sont un chat temps réel avec texte enrichi, le partage de fichiers, un contrôle d’accès fin, l’automatisation et l’intégration avec des outils externes, le chiffrement en transit, un chiffrement optionnel au stockage et l’authentification des utilisateurs
- Cette petite implémentation est possible parce qu’elle s’appuie sur des briques de base cohérentes et composables fournies par les implémentations UNIX modernes
- Le serveur Mattermost, à l’inverse, représente à lui seul environ 500 000 lignes de code Go ;
sucestime pouvoir reproduire les fonctionnalités centrales de Mattermost avec 0,005 % de ce volume de code
La boucle d’écriture de messages en 5 lignes
- La boucle Bash au cœur de
suclit une ligne depuis l’entrée standard, lui ajoute la date et le vrai nom de l’utilisateur, puis l’ajoute au fichier/var/lib/suc/"$1" - Le script réel est plus long à cause de prétraitements de sécurité et du calcul de la couleur du nom d’utilisateur, mais l’action centrale d’écriture du journal de chat se résume à trois étapes
- lire une ligne depuis l’entrée standard
- ajouter la date et le vrai nom de l’utilisateur
- l’ajouter au fichier du canal sous
/var/lib/suc/
- L’authentification, le contrôle d’accès, le chiffrement et le texte enrichi ne sont pas pris en charge par du code interne à
suc, mais par SSH, les API de contrôle d’accès UNIX et la modularité textuelle
SSH s’occupe de l’authentification
- Le processus
sucest exécuté par un utilisateur déjà authentifié ;suclui-même ne contient donc aucun code d’authentification - Pour l’authentification distante, c’est
sshqui s’en charge, comme sur un serveur UNIX classique- secrets partagés comme les mots de passe
- défis cryptographiques
- dispositifs de génération de mots de passe à usage unique (OTP)
- MFA combinant plusieurs méthodes
sshauthentifie aussi le serveur auprès du client afin d’empêcher les attaques de l’homme du milieu, et chiffre toutes les données entre client et serveur- L’installation de
sucdépend de l’hôte UNIX et de la configuration du serveurssh- l’utilisateur doit exister sur le système
- le serveur
sshdoit autoriser la connexion distante de cet utilisateur
- Avec GNU Guix, une configuration déclarative permet d’ajouter un utilisateur et sa clé SSH en une ligne, et si cette ligne disparaît de la configuration, l’utilisateur est supprimé lors de la reconfiguration
Le contrôle d’accès repose sur le modèle de permissions UNIX
sucn’inclut pas seulement aucun code d’authentification, mais pas non plus de code de contrôle d’accès ; cette approche est qualifiée de security agnosticism- Le noyau UNIX connaît les utilisateurs, groupes, processus et fichiers, et décide des droits de lecture et d’écriture en fonction du propriétaire effectif du processus
- Les fichiers de canal dans
/var/lib/sucappartiennent à l’utilisateursuc, et les droits de lecture par groupe permettent d’autoriser certains utilisateurs à lire certains canaux - Les utilisateurs ordinaires n’écrivent pas directement dans les fichiers ; ils exécutent
suc, et c’est le noyau qui gère l’autorisation ou le blocage - Dans des communautés plus restrictives, on peut disposer de binaires
setuidpar groupe- par exemple,
suc_blueappartient à l’utilisateurblueet seul le groupebluepeut l’exécuter - les utilisateurs du groupe
bluepeuvent écrire dans le canalblueviasuc_blue - les utilisateurs du groupe
redne peuvent ni lire ni écrire dans le canalblue
- par exemple,
- Le script auxiliaire de création de canal
suc_channel.shfait environ 80 lignes, et sous GNU Guix, les utilisateurs peuvent créer le binaire setuid, l’utilisateur, le groupe et le fichier de canal nécessaires avec une seule ligne :(suc-private-channel "red" "red") - Les mises à jour transactionnelles de GNU Guix garantissent qu’un changement de configuration est entièrement appliqué ou pas du tout, avec possibilité de revenir à l’état précédent
Texte enrichi et usuc
- Beaucoup d’applications de chat rendent le texte via un moteur HTML, et le client desktop Mattermost utilise Electron
sucutilise un fichier texte par canal, que l’utilisateur peut consulter avec des outils en ligne de commande commetailoucat- En exploitant les codes d’échappement ANSI du terminal et la prise en charge de l’UTF-8, on peut obtenir des couleurs, des émojis et une expérience de texte enrichi basique
sucne fait qu’écrire dans les fichiers de canal ; c’estusucqui se charge de la lecturesucest un binaire privilégié, donc sa logique et ses dépendances externes doivent rester minimalesusucs’exécute avec les droits de l’utilisateur appelant, ce qui permet d’y ajouter plus librement des fonctionnalités
usucassemble plusieurs petits outils pour jouer le rôle de client de chatrlwrapfournit l’historique et l’édition de ligne- sélection du binaire
sucsetuid approprié selon le propriétaire du fichier de canal tail -f -n 20affiche les 20 dernières lignes puis les nouvelles- vérification si la ligne saisie commence par
: - traitement du texte saisi par l’utilisateur avec
pygmentize
pygmentizeconsidère l’entrée standard comme du Markdown et produit du texte coloré ANSI, ce qui permet de prendre en charge des balises comme**bold**avec une seule ligne de code
Commandes de chat et intégration shell
- Si un message commence par
:,usucne l’envoie pas comme un message normal, mais exécute la commande correspondante puis transmet son résultat en sortie àsuc - Par exemple, exécuter
gum style --border=rounded --bold --foreground=#F00 "Hello World !"fait apparaître dans le canal un texte avec bordure et style - Les commandes s’exécutent dans l’espace de noms de l’utilisateur qui a lancé
usuc, ce qui permet à chacun d’avoir ses propres macros de chat sans affecter les autres - Sur the dam, c’est utilisé pendant des parties de jeu de rôle sur table pour lancer des dés avec des commandes comme
roll 2d6
Pipelines texte et intégration avec des outils externes
- Plutôt que d’utiliser l’exécution de commandes intégrée à
usuc, on peut aussi rediriger directement depuis le shell la sortie d’une commande verssuc - En envoyant à
sucla sortie colorée debat, on peut partager dans un canal des extraits de code avec coloration syntaxique make test > testlog || (suc devops < testlog ; exit 1)est un exemple de one-liner Bash qui envoie les logs au canaldevopsen cas d’échec des tests- Cette ligne peut être placée dans le hook
updated’un dépôt Git, afin de lancer les tests à chaque push, refuser la mise à jour en cas d’échec, puis prévenir l’équipe DevOps - Ce qui correspondrait aux fonctions d’intégration de Slack ou Mattermost peut ainsi être construit avec des pipelines texte UNIX et des scripts
Lecture des canaux, notifications et bots
- Les canaux
sucétant des fichiers texte continuellement mis à jour, on peut lire et traiter les nouvelles lignes avectail -f - On peut créer un pipeline qui affiche une notification via
notify-sendà chaque nouveau message, et filtrer avecgrepuniquement son nom ou des mots-clés comme « build failure » - Pour fusionner plusieurs canaux dans un seul flux, on peut utiliser
tail -f /var/lib/suc/* - Avec lnav, on peut mémoriser la position de lecture, poser des marque-pages, appliquer des couleurs par canal, analyser la date, le nom d’utilisateur et des champs personnalisés, filtrer et exécuter des requêtes SQL
- Un bot est possible dès lors qu’il peut lire et écrire dans les canaux
- l’exemple repère dans un canal une expression de longueur comme
33000 ft - la convertit en mètres avec la commande
units - puis écrit dans le même canal une réponse comme
[metric_bot] 33000 ft is 10058.4 meters.
- l’exemple repère dans un canal une expression de longueur comme
Éléments de comparaison et conclusion
- Le texte compare
sucà de gros systèmes comme Slack, Mattermost ou Discord, tout en estimant que des références plus équitables incluraient aussi IRC,talketwrite - IRC est jugé moins fonctionnel que
suc, car il faut un bouncer pour relire l’historique des conversations - Il est indiqué que
talk,ytalketwalln’ont pas pu être rendus fonctionnels sur des distributions Linux modernes sucdélègue l’authentification à SSH, le contrôle d’accès et la composabilité à UNIX, et conserve les canaux sous forme de fichiers texte- Tant qu’un outil peut lire et écrire du texte, il peut être développé dans n’importe quel langage
1 commentaires
Commentaires sur Hacker News
Cela fait immédiatement penser à « Master Foo and the Ten Thousand Lines » : http://catb.org/~esr/writings/unix-koans/ten-thousand.html
C’est l’histoire où Master Foo dit à un programmeur en visite qu’« une ligne de script shell contient plus de nature Unix que dix mille lignes de C »
Si la fonction centrale définie d’une chose peut être recréée trop facilement et de façon trop triviale, alors cette fonction n’est peut-être pas le cœur ni la source réelle de sa valeur
Je me demande quelle part du développement d’UI depuis les années 1980 a été absorbée et automatisée par l’ingénierie logicielle, ou si cela relève encore du domaine de l’art purement humain
Avant que Slack n’arrive et n’évince d’autres solutions, il existait déjà des dizaines d’apps, commerciales comme open source, qui faisaient cela
La valeur centrale de Slack se situe à l’étape suivante. Il offre une UI/UX visuellement agréable et très intuitive, de sorte que presque n’importe qui peut s’asseoir et l’utiliser immédiatement, sans apprentissage, configuration, manuel ni formation. Aucune connaissance technique n’est nécessaire, et il n’y a rien à comprendre au-delà de « entrer un nom d’utilisateur et un mot de passe » : le reste paraît évident
Mais cela soulève une question plus difficile. Comparé à l’alternative consistant à former davantage des utilisateurs capables d’effectuer un appel basique en ligne de commande, le prix de Slack vaut-il vraiment le coup ? Et si l’on tient compte du fait que Slack retient les données et ne les restitue pas ? Combien coûte le départ de Slack ?
suc est certes peu pratique, mais il semble possible de converger vers quelque chose de moins massif et moins propriétaire que Slack
J’aime bien la réalisation en elle-même. C’est sympa de voir ce qu’on peut fabriquer avec la composabilité
En revanche, la comparaison avec Slack et compagnie ne me parle pas vraiment. Aucun ingénieur n’aurait envie d’être responsable en production d’un assemblage de ce genre. Comment recruter les talents, comment tester et déboguer, comment gérer les logs et l’analytique ?
Cet article reproduit une partie de la fonctionnalité de chat des apps de type Slack, mais le chat ne représente pas 100 % de ce que sont ces apps. Elles ont été conçues dans des contextes totalement différents, donc attaquer l’existant comme étant obèse ressemble à une interprétation tordue à la Stockton Rush. Il y a bien de l’obésité dans le logiciel, mais « regardez maman, j’ai fabriqué un sous-marin avec de la bricole, donc les sous-marins coûteux sont idiots » ne ressemble pas à la réfutation que l’auteur cherchait à faire
Les utilitaires individuels sont généralement plus faciles à comprendre, tester et déboguer séparément, et la journalisation est déjà intégrée au système. syslog n’existe-t-il pas ? Et pourquoi aurait-on besoin d’analytique ?
Cela dit, je me demande vraiment à quoi ressemblerait un outil comme Slack s’il donnait agressivement la priorité à la simplicité plutôt qu’aux nouvelles fonctionnalités. On dirait que le capitalisme, dans son ensemble, n’encourage pas ce type de développement, donc il existe peu d’exemples de logiciels d’entreprise aussi simples qu’il serait raisonnablement possible de les rendre. Cela reste néanmoins une expérience de pensée intéressante
Dans le même genre, il y a aussi ii, qui fonctionne directement au-dessus d’IRC : https://tools.suckless.org/ii/
Il utilise un seul fichier standard pour toutes les conversations, l’authentification et le contrôle d’accès sont gérés directement par le réseau IRC, et il existe déjà de nombreuses UI. Comme dans cet article, les plugins peuvent être n’importe quoi qui lit certains fichiers et écrit dans d’autres fichiers
Ce serait amusant de faire un diagramme spawn of unix rassemblant des startups découpées dans les interstices des fonctions Unix, à la manière du diagramme de craigslist
https://thegongshow.tumblr.com/post/345941486/the-spawn-of-craigslist-like-most-vcs-that-focus
ftp/rsync => Dropbox
suc => Slack
...
Si le titre vous semble assez putaclic pour que vous ayez envie de passer votre chemin, il l’est effectivement un peu, mais c’était en réalité un très bon billet.
Il traite d’un utilitaire appelé « suc » (Simple Unix Chat), qui implémente des fonctionnalités serveur à la Slack ou Discord avec une base de code minuscule.
La nouveauté, c’est qu’au lieu de réinventer les outils et la méthodologie Unix existants, il les exploite. L’authentification est gérée par SSH, les canaux ne sont que des fichiers, et le contrôle admin/modération repose sur les groupes d’utilisateurs et les permissions de fichiers. Pour le texte enrichi ou l’upload de fichiers, il suffit d’écrire les données voulues dans le canal et de laisser le client les interpréter. Même les bots deviennent très simples si on relie des pipes aux fichiers de canal.
On ne peut pas créer un clone parfait de Slack avec les cinq lignes montrées dans l’article pour écrire un message dans un canal, mais c’est vraiment impressionnant de voir jusqu’où on peut aller avec des outils Unix aussi simples.
S’il n’existe pratiquement aucun système de production à grande échelle écrit avec le paradigme « tout est fichier » et des scripts shell, ce n’est pas un hasard. Pour tout système non trivial destiné à un usage réel, c’est inacceptable.
Avec une UI d’administration, une UI de gestion des utilisateurs, de l’extensibilité, des webhooks, etc. Dire « tout le monde se connecte à cette machine et peut discuter avec les autres utilisateurs sous Linux » ne couvre rien de tout cela. Ce genre de choses existe sur tous les *nix depuis avant la naissance de la plupart des lecteurs ici.
Malheureusement, cette fonctionnalité de Plan 9 n’a pas encore été portée sur Linux. Ce sont des bits de permission comparables à « lecture » ou « écriture », mais beaucoup plus spécifiques.
Ce n’est pas un reproche à l’auteur, et l’article le reconnaît d’ailleurs très honnêtement.
Slack et Discord ont le partage d’écran et le chat vocal. Ce ne sont pas des fonctionnalités annexes, mais des éléments assez décisifs dans leur domination du marché.
Le billet de blog reconnaît honnêtement dès le départ qu’il porte sur suc et que le titre peut être un peu trompeur, mais au final il véhicule aussi un message important qui n’a pas de lien direct avec suc.
Presque tous les logiciels modernes sont atrocement boursouflés. L’idée qu’il faille 1,7 million de lignes pour un serveur de chat riche en fonctionnalités est absurde. Pour paraphraser Bill Gates, même en essayant, je ne pense pas être capable de « consommer » autant de lignes de code pour un serveur de chat.
J’applaudis non seulement le fait d’avoir soulevé le problème ou proposé une alternative, mais aussi le rappel qu’il n’est pas nécessaire de réinventer la roue. Implémenter intelligemment en s’appuyant sur les propriétés des systèmes et sous-systèmes existants — par exemple utiliser SSH pour l’authentification et le chiffrement — même si ce n’est pas leur usage initialement imaginé, tant que cela respecte l’intention de ces propriétés, c’est un très bon exemple de l’esprit hacker.
J’aimerais que le monde du logiciel tire davantage de cette idée. Des coûts plus faibles, des délais de développement plus courts, moins de temps d’ingénierie gaspillé à réécrire un code fonctionnellement équivalent : tout cela est bénéfique pour les développeurs, les entreprises, les utilisateurs finaux et, en fin de compte, l’humanité entière. J’aimerais voir davantage de destruction créatrice de ce type, et j’aimerais moi-même appliquer ce genre de techniques astucieuses dans de futurs projets.
Donc, en pratique, il serait plus juste de ne compter que les lignes de code Go.
[0] https://github.com/mattermost/mattermost/tree/master/e2e-tests
[1] https://github.com/mattermost/mattermost/tree/master/webapp
C’est en général une bonne idée, car cela accélère la mise sur le marché et réduit la taille de l’organisation. Mais cela implique un gros compromis en matière de complexité et d’abstraction. Et plus le niveau d’abstraction monte, pire cela peut devenir. Donc ce n’est pas totalement sans raison.
Actuellement, la plupart des distributions empêchent, pour des raisons de sécurité, de créer des scripts bash setuid
Mais on peut obtenir presque le même effet avec une entrée spécifique dans
sudoersuid != euidhttps://perldoc.perl.org/perlsec#Taint-mode
Certains systèmes peuvent avoir une condition de concurrence fondamentale :
https://perldoc.perl.org/perlsec#Shebang-Race-Condition
L’usage de
sudo -Tvaut aussi le détour :https://perldoc.perl.org/perlsec#Using-Sudo
C’est dommage que le mode taint ait récemment été supprimé de Ruby. Le suivi de la contamination est un mécanisme puissant qui va bien au-delà des scripts. Par exemple, si le
#html_safede Rack/Rails avait été géré via le suivi de contamination, cela aurait été plus rigoureux : au moment même où une chaîne utilisateur contaminée était concaténée/interpolée/formatée dans un fragment SQL, ça aurait explosé, ce qui aurait permis de bloquer par conception toute une catégorie d’injections SQLIl y a longtemps, certains utilisaient aussi le suivi de contamination de manière très efficace avec CGI/mod_perl. Ce n’est absolument pas une solution miracle, mais cela reste un outil assez efficace
https://gitlab.com/edouardklein/suc/-/blob/master/suc_wrapper.c
setuidne s’appliquait pas, parce que ce qui est exécuté n’est pas le fichier lui-même, mais la cible indiquée dans le shebangAvant de voir le fichier wrapper dans le dépôt GitLab, j’espérais en lisant qu’il y ait une méthode
C’est un autre sujet, mais je suis surpris qu’il n’ait pas été fait mention de talk(1) : https://man.netbsd.org/talk.1
L’avantage, c’est qu’on peut limiter la réception au local uniquement, ce qui impose alors d’avoir un compte utilisateur sur cette machine. C’est donc bien adapté à des endroits comme SDF ou à des clusters plus importants. Pas besoin de gérer soi-même l’authentification : on peut la laisser à PAM ou BSD_Auth. Pas besoin non plus de gérer soi-même les sockets ou le chiffrement : on peut laisser ça à TLS. On peut aussi imaginer entrer dans un shell sécurisé via SSH, puis utiliser tmux et (n)talk
Bien sûr, (n)talk est écrit en C, donc ce n’est pas 5 lignes de bash
[1] https://man7.org/linux/man-pages/man2/write.2.html