3 points par GN⁺ 2023-07-06 | 1 commentaires | Partager sur WhatsApp
  • suc est 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, suc souligne qu’un comportement central similaire peut être obtenu avec un code bien plus réduit
  • usuc combine tail -f, rlwrap, pygmentize et 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

  • suc signifie 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 ; suc estime 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 suc lit 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 suc est exécuté par un utilisateur déjà authentifié ; suc lui-même ne contient donc aucun code d’authentification
  • Pour l’authentification distante, c’est ssh qui 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
  • ssh authentifie 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 suc dépend de l’hôte UNIX et de la configuration du serveur ssh
    • l’utilisateur doit exister sur le système
    • le serveur ssh doit 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

  • suc n’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/suc appartiennent à l’utilisateur suc, 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 setuid par groupe
    • par exemple, suc_blue appartient à l’utilisateur blue et seul le groupe blue peut l’exécuter
    • les utilisateurs du groupe blue peuvent écrire dans le canal blue via suc_blue
    • les utilisateurs du groupe red ne peuvent ni lire ni écrire dans le canal blue
  • Le script auxiliaire de création de canal suc_channel.sh fait 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
  • suc utilise un fichier texte par canal, que l’utilisateur peut consulter avec des outils en ligne de commande comme tail ou cat
  • 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
  • suc ne fait qu’écrire dans les fichiers de canal ; c’est usuc qui se charge de la lecture
    • suc est un binaire privilégié, donc sa logique et ses dépendances externes doivent rester minimales
    • usuc s’exécute avec les droits de l’utilisateur appelant, ce qui permet d’y ajouter plus librement des fonctionnalités
  • usuc assemble plusieurs petits outils pour jouer le rôle de client de chat
    • rlwrap fournit l’historique et l’édition de ligne
    • sélection du binaire suc setuid approprié selon le propriétaire du fichier de canal
    • tail -f -n 20 affiche 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
  • pygmentize considè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 :, usuc ne 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 vers suc
  • En envoyant à suc la sortie colorée de bat, 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 canal devops en cas d’échec des tests
  • Cette ligne peut être placée dans le hook update d’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 avec tail -f
  • On peut créer un pipeline qui affiche une notification via notify-send à chaque nouveau message, et filtrer avec grep uniquement 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é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, talk et write
  • IRC est jugé moins fonctionnel que suc, car il faut un bouncer pour relire l’historique des conversations
  • Il est indiqué que talk, ytalk et wall n’ont pas pu être rendus fonctionnels sur des distributions Linux modernes
  • suc dé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

 
GN⁺ 2023-07-06
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

    • C’est vrai. Par exemple, la principale valeur ajoutée de Slack n’est pas « des utilisateurs authentifiés s’envoient des messages »
      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
    • C’est un point de vue très pertinent. Dans ce cas, la valeur de Slack est sa convivialité, et suc n’est clairement pas destiné aux utilisateurs ordinaires
      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

    • L’idée centrale, c’est qu’une réduction de la surface du codebase rend une grande partie de ces préoccupations moins importantes
      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 ?
    • C’est aussi comme ça que je l’ai lu. C’était une excellente démonstration d’ingénierie et très agréable à lire, mais il aurait été plus honnête de comparer cela non pas à Slack, mais simplement à IRC
      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.

    • Les plateformes de chat existantes n’ont pas « réinventé » les outils Unix, elles ne les ont pas utilisés parce qu’ils ne sont pas adaptés à ce qu’elles veulent construire.
      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.
    • Pour la plupart des utilisateurs finaux, Slack est un client.
      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.
    • Les fichiers append-only sembleraient bien adaptés ici. Ils garantiraient que les utilisateurs ordinaires et les bots ne puissent pas modifier les messages des autres.
      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.
    • « Exploiter les outils et méthodes Unix existants » finit presque toujours par vouloir dire « implémenter X avec un nombre de lignes de code incroyablement faible ».
      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é.

    • Décisifs, oui, mais j’ai l’impression que leur qualité se dégrade de plus en plus.
  • 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.

    • À titre de référence, la plupart des lignes de code TypeScript se trouvent dans les tests E2E[0] et le répertoire webapp[1]. Comme son nom l’indique, webapp contient « le code client de l’application web Mattermost ».
      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
    • J’ai déjà assisté à une réunion pour choisir un framework d’authentification web pour un site ; ça s’éternisait à cause du bikeshedding, alors j’ai fini par en coder un basé sur les mécanismes d’authentification déjà fournis par HTTP pendant la réunion, avec une démo prête avant la fin.
    • Sur le principe je suis d’accord, mais une bonne partie de cette boursouflure vient du fait qu’on essaie de prendre en charge plusieurs plateformes avec une base de code unique.
      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 sudoers

    • En réalité, ce n’est pas seulement bash, mais presque tous les scripts shebang ; la principale exception, ce sont les scripts Perl. Perl active automatiquement le mode taint quand uid != euid
      https://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 -T vaut 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_safe de 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 SQL
      Il 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
    • Utiliser un wrapper C qui appelle le script shell. Ce n’est pas très élégant visuellement et il faut un peu plus durcir le script, mais ça fonctionne :)
      https://gitlab.com/edouardklein/suc/-/blob/master/suc_wrapper.c
    • Il existe vraiment des distributions qui autorisent ça ? Tout ce que j’avais lu quand j’ai voulu essayer disait que setuid ne 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 shebang
      Avant 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

    • Ou encore write(2)[1]. Moi, j’ai tout de suite pensé à ntalk, parce que c’était la dernière fois que j’avais utilisé ce genre de chose sur la même machine. À l’époque, IRC n’était pas chiffré
      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