Le site arthur.bebou.netlib.re - retour accueil
git clone git://bebou.netlib.re/arthur.bebou
Log | Files | Refs |
commit 4a25c18c69ab0bb5fe97ba1ec3fa27643f2d5a28 parent 77d1f5ea32d4b7b559895df9645b3ee2e4fe43ab Auterice: Arthur Pons <arthur.pons@unistra.fr> Date: Mon, 10 Jun 2024 17:01:50 +0200 Fin article services sobres avant relecture Diffstat:
M | contents/services-sobres/index.sh | | | 284 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
1 file changed, 281 insertions(+), 3 deletions(-)
diff --git a/contents/services-sobres/index.sh b/contents/services-sobres/index.sh @@ -2,10 +2,12 @@ title: Des services internet simples et sobres author: Arthur Pons description: Étudions comment construire et mettre en ligne des services relativement simples et sobres sur internet -publication: 2024-06-07 +publication: 2024-06-10 sectionmd: main +Cet article n'a pas encore été relu, un peu d'indulgence :) + ## Le "trop-long-j'ai-pas-lu" : Sur un système unix possédant netcat[^1] vous pouvez faire @@ -213,7 +215,281 @@ cherche, en se basant sur ce que l'on a appris précédemment, à implémenter l minimum nécessaire sur le serveur pour rendre ces commandes disponibles à distance de manière non authentifiée et sécurisée. L'idée est que la construction de services sous cette forme ne nécessite presque rien de plus que -de faire fonctionner les commandes associées en local. Contrairement +de faire fonctionner les commandes associées en local contrairement à une appli +web ou même un cgi. + +Si l'on reprend l'exemple + + cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 1234 > /tmp/f + +on peut modifier le filtre `/bin/sh -i 2>&1` par autre chose. Si l'on veut un +service qui modifie toutes les `armes` par des `fleurs` on pourra écrire : + + $ cat /tmp/f | sed 's/armes/fleurs/g' | nc -l 127.0.0.1 1234 > /tmp/f[^8] + +Ce qui donnera côté client + + $ nc bebou.netlib.re 1234 + Ils prirent les armes + +Ah, ça ne fonctionne pas. En effet sed, comme pleins d'autres commandes, ne +traite pas les lignes une à une mais en stocke quelques une avant de les faire +passer à la moulinette. Il constitue un "buffer", on dit parfois en franglais +qu'il "buffer" son entrée. Pour résoudre ce problème deux solutions. Soit +l'outil intègre une option pour ne pas bufferiser (`--unbuffered` pour GNU sed) +soit on utilise la commande `stdbuf` qui dira à la commande qui lui est passée +en argument qu'il ne faut rien bufferiser : + + $ cat /tmp/f | stdbuf -o0 sed 's/armes/fleurs/g' | nc -l 127.0.0.1 1234& > /tmp/f + $ nc bebou.netlib.re 1234 + Ils prirent les armes + Ils prirent les fleurs + +Super ! Nous en savons plus sur comment traiter les données qui arrivent dans le +socket du serveur mais nous n'avons toujours pas créé le système que nous +voulions. Pour ce faire reprenons l'exemple qui était donné dans le manuel en +retirant l'aspect interactif et en ajoutant stdbuf : + + $ cat /tmp/f | stdbuf -o0 sh | nc -l 127.0.0.1 1234& > /tmp/f + $ nc bebou.netlib.re 1234 + date + lun. 10 juin 2024 16:04:52 CEST + +### Avec un peu de securité + +Sans pour autant récupérer un shell, nous pouvons exécuter des commandes. +Pour des raisons évidentes de sécurité nous voulons restreindre ces commandes à +celles que nous mettons à disposition, par exemple `conjuguer`. Pour cela nous +pouvons utiliser grep pour filtrer les entrées avant de les passer à `sh` : + + $ cat /tmp/f | stdbuf -o0 grep -E '^conjuguer' | stdbuf -o0 sh | nc -l 127.0.0.1 1234& > /tmp/f + $ nc bebou.netlib.re 1234 + ls + conjuguer manger + Indicatif Présent je mange + Indicatif Présent tu manges + Indicatif Présent il mange + Indicatif Présent nous mangeons + Indicatif Présent vous mangez + +C'est mieux, on ne peut plus exécuter d'autres commandes que conjuguer. A moins +que... + + $ nc bebou.netlib.re 1234 + conjuguer;date + lun. 10 juin 2024 16:05:17 CEST + +A mince. Le grep n'a pas retiré la ligne puisqu'elle commence bien par +"conjuguer". Le shell a ensuite tenté d'exécuter `conjuguer` sans argument (en +retournant certainement une erreur) puis a compris le point virgule comme +délimitant une nouvelle commande, en l'occurrence `date`, qu'il a exécuté. + +Le `sh` du serveur sur lequel tout cela tourne est dash. J'ai donc consulté sont +manuel pour connaître les caractères spéciaux : + +> Lexical Structure +> The shell reads input in terms of lines from a file and breaks it up into words at +> whitespace (blanks and tabs), and at certain sequences of characters that are special +> to the shell called “operators”. There are two types of operators: control operators +> and redirection operators (their meaning is discussed later). Following is a list of +> operators: +> +> Control operators: +> & && ( ) ; ;; | || <newline> +> +> Redirection operators: +> < > >| << >> <& >& <<- <> + +et + +> Reserved Words +> Reserved words are words that have special meaning to the shell and are recognized at +> the beginning of a line and after a control operator. The following are reserved +> words: +> +> ! elif fi while case +> else for then { } +> do done until if esac + +Une manière un peu bourrine mais je pense, du moins j'espère, assez fiable est +de supprimer toutes les occurrences de ces mots et caractères. Nous aurions donc +à minima pour les opérateurs et autres caractères importants : + + $ cat /tmp/f | + stdbuf -o0 tr -d '$`&(;|<>' | + stdbuf -o0 grep -E '^conjuguer' | + stdbuf -o0 sh | + nc -l 127.0.0.1 1234& > /tmp/f + +Je n'ai pas la certitude que cela empêche toute commande dangereuse d'arriver à +la ligne `sh`. La bonne approche serait certainement de créer une commande +spéciale qui parse son entrée et sur la base de certains critères exécute +certaines commandes générées par nos soins plutôt que de tenter d'assainir les +commandes entrées par l'utilisateur·ice. En attendant le service étant utilisé +par très peu de personnes et la survie du serveur n'étant pas absolument +primordiale[^9] j'ai décidé que c'était suffisant. Je vais soumettre l'exercice +à des personnes motivées pour tenter de trouver des failles. + +Finalement on pourra y ajouter des logs à coup de `tee` : + + $ cat /tmp/f | + stdbuf -o0 tee prefilter.log | + stdbuf -o0 tr -d '$`&(;|<>' | + stdbuf -o0 grep -E '^conjuguer' | + stdbuf -o0 tee postfilter.log | + stdbuf -o0 sh | + nc -l 127.0.0.1 1234& > /tmp/f + +On peut ajouter des commandes disponibles en les installant sur la machine et en +ajoutant des alternatives à la regex du grep : `^(conjuguer|synonyme)`. On peut +également maintenir un fichier des commandes autorisées, une commande par ligne, +et faire : + + stdbuf -o0 grep -wf authorized + +## Quelques commentaires, bonus et perspectives pour rendre ça un peu sympa + +### socat + +Dans les faits et pour plusieurs raisons que j'ai déjà oublié j'utilise côté +serveur `socat`. Les commandes vues précédemment sont les mêmes, à la suite d'un +`cat`, mais existent dans un script séparé qui est renseigné dans la commande +socat qui lance le serveur : + + socat -d -d -lf /var/log/socatlog tcp-listen:2222,fork exec:script + +TODO : éclaircir pourquoi j'ai finalement choisi socat. +En tout cas cet outil score probablement un peu moins bien dans les critères vus +[ici](/services-sobres/#quelle-forme-pour-ces-services-) (32k lignes par ex) +mais est plus polyvalent. + +### Une commande help + +Une commande `help` est une bonne idée ne serait-ce que pour lister les +commandes dispos. Pour qu'elle s'affiche au lancement d'une session on peut +ajouter un + + echo "help pour la liste des commandes" + +au début du script. Cela à pour inconvénient d'ajouter cette ligne dans les +résultats de commandes lancées non interactivement. Exemple : + + $ nc bebou.netlib.re 2222 + help pour la liste des commandes + help + h[elp] + + cette aide + + s[ynonyme] mot + + la liste des synonymes de "mot" + [...] + s table + tableau 14 + répertoire 14 + pupitre 13 + liste 11 + catalogue 11 + [...] + +mais si l'on passe la commande directement dans stdin (`-N` dira à nc d'envoyer +un caractère de fin de fichier à la fin afin de terminer la connexion et +récupérer la main) : + + $ echo "s table" | nc -N bebou.netlib.re 2222 + help ou h pour obtenir de l'aide + tableau 14 + répertoire 14 + pupitre 13 + liste 11 + catalogue 11 + [...] + $ + +On voit que l'on se traine l'aide. Il y a peut-être un moyen d'y remédier côté +serveur mais en attendant je script mes usages non interactifs de façon à +supprimer la première ligne de résultat. Par exemple, le script zsh que +j'utilise pour la conjugaison (noter le `sed '1d'` qui supprime la première +ligne) : + + read ?"Verbe (et filtres) : " verbe + echo "conjuguer -c $verbe" | nc -N bebou.netlib.re 2222 | sed '1d' + read -k1 + +### Des scripts pour un peu plus d'interactivité + +En jouant avec le service de synonyme je me suis rendu compte qu'il était assez +naturel de vouloir sélectionner l'un des synonymes proposer pour voir ses +synonymes et ainsi de suite, à la manière de la navigation dans un document avec +des liens hypertexte. Il aurait été dommage de perdre totalement cette +fonctionnalité là. Pour cela j'ai opté de ne pas complexifier la commande côté +serveur mais de l'implémenter côté client : + + read ?"Mot : " mot + while [ -n "$mot" ] + do + oldmot=$mot + echo $oldmot | cut -f1 + mot=$(echo "synonyme $mot" | nc -N bebou.netlib.re 2222 | sed '1d' | fzy) + done + read -k1 + +Ce script donne ce genre de session de navigation : + + Mot : table + table + > + tableau 14 + répertoire 14 + > pupitre 13 + liste 11 + +Puis après sélection de pupitre : + + Mot : table + table + pupitre + > + > lutrin 50 + console 20 + chaire 20 + table 13 + bureau 4 + +Une fois la navigation terminée on peut en sortir en appuyant sur "echap". +Pour les personnes initié·es voici mon menu [zenu](/zenu/) correspondant : + + _conjuger + _synonyme + ## pre + ## react + ;; (c) + read ?"Verbe (et filtres) : " verbe + echo "conjuguer -c $verbe" | nc -N bebou.netlib.re 2222 | sed '1d' + read -k1 + ;; (s) + read ?"Mot : " mot + while [ -n "$mot" ] + do + oldmot=$mot + echo $oldmot | cut -f1 + mot=$(echo "synonyme $mot" | nc -N bebou.netlib.re 2222 | sed '1d' | fzy) + done + read -k1 + +### Favoriser le local + +Malgré l'existence de ces services que je compte bien continuer à maintenir je +maintiens ma volonté que les personnes autour de moi les installent en local. +Pour favoriser cela je devrais : + + * En faire des paquets debian + * Mieux les documenter (c'est un peu le foutoir là) + * Publier des menus zenu directement dans les dépôts git et/ou dans les + paquets debian + * Intégrer quelque chose au service qui permette à minima de visibiliser que + c'est possible ? [^1]: sur debian faire `sudo apt install netcat-openbsd` si vous ne l'avez pas déjà. En réalité tout outil permettant de se connecter à un port sur une machine distante. `socat` fonctionne, `telnet` aussi etc. [^2]: article plus très à jour par rapport à ce que je fais aujourd'hui mais passons @@ -221,4 +497,6 @@ de faire fonctionner les commandes associées en local. Contrairement [^4]: en écrivant ces lignes je me rends compte qu'ils n'ont rien d'évident. Notamment celle-ci. L'intuition première est que peu de personne = projet simple et facile à maintenir, pleins de personnes = projet complexe et difficile à maintenir. Je pense que cette intuition n'est pas totalement fausse mais il faut envisager d'autres paramètres. Une personne peut, si elle est très productive, construire des logiciels qui ne sont raisonnablement maintenu par la suite que par une équipe de personne (eg Fabrice Bellard avec qemu et ffmpeg). Il est également possible qu'un outil soit créé et maintenu par une seule personne sans être pour autant documenté ou partagée. A moins que cet outil soit très simple sa maintenance posera problème. D'une certaine manière l'existence d'une équipe, et donc de plus de monde, autour d'un outil peut offrir de la résilience en cas de perte d'une personne. Ça n'est probablement qu'en combinant les variables du nombre de personnes actives pour maintenir l'outil, le nombre de personnes connaissant bien son fonctionnement et la documentation associée que l'on parviendrait à dégager un avis pertinent sur le rapport entre personnes impliquées et soutenabilité. Il faudrait également y inclure des variables économiques telles que la pérennité des financements du projet s'ils existent, la popularité des technologies utilisées etc. Bref c'est bien plus compliqué que ce que ces cinq critères de base laissent penser. [^5]: un jour peut-être [^6]: oui oui, si vous avez l'habitude des manuels des outils GNU ça paraît fou. -[^7]: je dois avouer que je n'étais pas très bon élève lors de mes études mais je n'ai pas souvenir que l'on m'ait enseigné des protocoles aussi fondamentaux qu'HTTP et SMTP on me faisant expérimenter de la sorte. Tout conservait un aura un peu mystérieuse. On m'a pourtant enseigné TCP dans les détails. +[^7]: je dois avouer que je n'étais pas très bon élève lors de mes études mais je n'ai pas souvenir que l'on m'ait enseigné des protocoles aussi fondamentaux qu'HTTP et SMTP en me faisant expérimenter de la sorte. Tout conservait une aura un peu mystérieuse. On m'a pourtant enseigné TCP dans les détails. +[^8]: évidemment la regex ne couvre pas tous les cas, c'est un exemple +[^9]: il ne serait pas très long de le réinstaller tel quel d'autant plus que tout est backupé