Master 2, Bases de données avancées, année 2021

Objectif

Rappels sur bash et l’environnement

Dans bash (le terminal, pour faire simple), il est possible de fixer certaine variable a certaine valeurs:

a=42

Donne à la variable a la valeur 42.

Il est possible d’utiliser cette valeur dans toute commande ce qui est bien pratique pour éviter de se souvenir de certaines informations. Pour utiliser une variable, on utilise la syntaxe $nom_variable. Donc par exemple:

echo La variable a vaut: $a

retourne

La variable a vaut: 42

Certaines variable sont local au processus bash en cours et d’autres sont des variables d’environnements qui contiennent des informations que les programmes utilisent pour personnalisé leur intéraction avec l’utilisateur.

Pour afficher les variables d’environnement, il suffit d’utiliser

env

Pour exporter la variable locale a en une variable d’environnement il suffit d’utiliser

export a

Cet export n’est valable que pour le processus (et les sous-processus) qui ont réalisé l’export.

Pour ne pas avoir a se rappeler des variables comme les adresse IP, les mot de passes, ect, il est possible de mettre ces information dans un fichier texte:

vm1=192.100.00.11 # une addresse ip
pass1=1234 # un très mauvais mot de passe

Il est possible de charger ces variables à l’aide de la commande source chemin/fichierchemin/fichier est le chemin vers votre fichier.

Dans les TP, j’utilise ces notations pour les commandes. Vous pouvez:

  1. Soit substituer à la main les variables (avec $) présente dans les commandes fournies
  2. Soit fixé les variables dans le shell ou dans l’environnement une fois et faire des copié/coller

Installation Redis

  1. Créez une nouvelle VM dédiée à Redis que vous nommez redis.

Dans la suite, on nommera $redis_ip l’adresse IP de cette VM.

  1. Installez Redis et à l’aide de la documentation configurez les accès distants pour autoriser les requêtes

Redis est packagé dans les dépots et peut donc être installé très simplement (et rapidement):

Oubliez pas de rafraichir les dépôts avec sudo apt update

$ sudo apt install redis

Vérifiez que redis est installé à l’aide de la commande

redis-cli --version

  1. Modifier le fichier de config /etc/redis/redis.conf pour:

Il est possible d’avoir une gestion plus fine des accès, mais ce n’est pas la peine de rentrer dans ce genre de détail ici (pour les curieux)

  1. À l’aide de redis-cli faite les commandes suivante:

À quoi sert la commande SELECT ?

Utilisation simple de Redis

Redis fournit une interface simple. On peut s’y connecter en TCP/IP et envoyer des message et avoir la réponse. Il est donc possible de bricoler un client très rapidement, quelque soit l’environnement.

Par exemple, en ligne de commande il est possible d’utiliser telnet qui offre une connexion (non chiffrée).

Ne fait jamais ça dans des vrais cas d’usage, un trafic réseau non chiffré implique que les mots de passes ne sont plus sécurisé. Il vaut mieux utiliser le client fourni.

À l’aide de telnet, connecté vous à l’addresse IP de redis au port par défaut (6379).

telnet $redis_ip 6379

Vous pouvez alors écrire des commandes simple. Vérifier que ça marche correctement.

Pour quitter, utiliser la commande redis QUIT.

n’utilisez plus telnet, c’est juste pour montrer la simpliciter du protocole

Utilisation applicative de Redis

La plupart du temps, un langage de programmation fournira un connecteur à Redis. On a vu, il est pas trop dure d’en bricoler un si ce n’est pas le cas, mais ça reste un bricolage à ne pas utiliser en production. En Python, on peut utiliser le module redis.

Pour l’installer, pip3 install --user redis devrait marcher quelque soit la machine, même sans les droits administrateurs. Attention, pour installer Redis sur tout le système utilisez la commande sudo pip3 install redis. Cela peut être utile pour avoir Redis sur une VM.

import redis
r = redis.Redis(host="100.42.42.42", port=6379, password="chocolat", db=1) # changer l'addresse ip et le mot de passe
r["test_python"] = 42

Vous pouvez vérifier que la clef test_python est bien arrivé dans la base de donnée.

À propos du type des clef et des valeurs

Dans redis, les clefs et les valeurs sont des suites de bytes et non typés.

Les clients souvent encode ça sous forme d’un type textuel ce qui peut entraîner des clash indésirable.

Par exemple, dans le client Python

r["42"] = "La réponse à la grande question"
r[42] = 0

Alors r["42"] retournera 0, car le client à transformer l’entier 42 en une chaine de caractère.

Il est tout à fait possible d’utiliser des fichiers binaires comme valeurs (et même comme clef, même si c’est déconseillé). Ainsi on peut stocker des images facilement:

with open("mon_logo.png", "rb") as f:
    r["mon_logo"] = f.read()

Les intergiciels (ou middleware)

La majorité du temps la base de données n’est pas exposé directement sur le réseau mais accessible via un intergiciel.

Le rôle de ce dernier est de faire le lien entre les clients et les bases de données, de distribuer la charge quand ces dernières sont distribuées. Un integiciel peut être dépendant d’un SGBD ou être fait à la main en fonction des besoins architecturaux. La plupart des SGBD proposent des connecteurs dans toutes les langages de programmation donc il est aisé d’en construire dans votre langages de prédilection.

Par défaut, Python fait bien l’affaire, mais le plus commun est en fait PHP. Il s’interconnecte facilement avec les serveurs web standards (comme Apache) et possède un gros vivier de développeur. Ici nous allons allez au plus simple:

On va construire un petit mécanisme CGI en bash, mais c’est simple d’adapter ça au langage de programmation de votre choix.

CGI est très simple à mettre en place, mais très peu efficace car nécessite un processus par requête HTTP. fastCGI permet de contourner ce goulôt d’étranglement en ajoutant un serveur intermédiaire qui s’occupe d’exécuter le code dans des processus qui tourne en permanence. En Python, il est aussi possible d’utiliser WSGI.

  1. Déployez une nouvelle VM nommée middleware et installer lighttpd.

Dans la suite on note $middleware_ip l’addresse ip de cette VM.

  1. Vérifier que lighttpd fonctionne en créant une page html contenant Hello World! dans /var/www/html/ et en exécutant localement:
curl $middleware_ip

Vous devriez avoir Hello World! comme réponse.

  1. Activez le mode mod_cgi à l’aide de la commande lighty-enable-mod et ajoutez les lignes suivantes au fichier de configuration du module (qui est /etc/lighttpd/conf-available/10-cgi.conf.
cgi.assign      = (
        ".cgi"  => "/usr/bin/bash",
)

Ainsi vous indiquez à lighttpd quel interpréteur utiliser pour exécuter les fichiers .cgi. Rechargez la configuration à l’aide de service lighttpd force-reload.

  1. Créez un script Bash nommé hello.cgi dans le dossier /var/www/html contenant
echo This is CGI!

Vérifier à l’aide de curl $middleware_ip/hello.cgi que le fichier est bien exécuté par lighttpd. Vous devriez avoir uniquement This is CGI comme réponse et pas echo.

  1. Créez un script env.cgi dans le dossier /var/www/html contenant la commande printenv. Que retourne curl $middleware_ip/env.cgi ?

Voici des erreurs courantes:

Les API REST

Une API Rest est une API dédié au web qui respecte le protocole HTTP et offre des garanties supplémentaires.

Les méthodes HTTP s’appuie sur trois composantes:

Dans la requête, il est possible d’ajouter des données qui sont transmise via stdin au script cgi.

Il est possible de communiquer avec un serveur, simplement en ligne de commande avec la commande curl. À l’aide de l’option -X (ou sa version longue --request on peut ainsi faire une requête POST, PUT, PATCH et DELETE).

  1. Ajoutez cat <& 0 à env.cgi et exécutez curl --data "Holla Mundo!" $middleware_ip/env.cgi?halloWelt. En conclure comment les données GET et POST sont transmises à l’application en CGI.

Les intergiciels en Bash sont a éviter à tout pris pour les projets important, tout comme CGI qui souffre de vrai problème de performances. En pratique, il faudrait utiliser FastCGI ou une autre technologie récente et un langage de programmation adapté. Typiquement Python propose de nombreux framework pour déployer des intergiciels (voir par exemple flask)

Réécriture d’URL

Il est très souvent utile de permettre à un utilisateur d’utiliser des url simple et de les rediriger.

Activer le module mod_rewrite et ajouter au fichier de configuration /etc/lighttpd/conf-available/10-rewrite.conf la ligne url.rewrite-once = ( "^/(.*)" => "/env.cgi" ) qui redirige toutes les requêtes vers le fichier cgi.

Exécuter la commande curl $middleware_ip/une/url/foireuse?avec=donnée. En analysant le retour, indiquer quelle variable d’environnement stock l’URL initiale avec réécriture par lighttp.

CGI avec Python

Pour ceux d’entres vous qui le souhaitent, il est possible d’utiliser Java.

  1. Enlever les règles de réécritures précédentes, nous allons avoir besoin d’en construire de nouvelles.

  2. Modifier /etc/lighttpd/conf-available/10-cgi.conf pour n’autoriser que les fichiers Python

cgi.assign      = (
        ".py"  => "/usr/bin/python3",
)
  1. Modifier rewrite pour rediriger l’URL env vers le fichier /env.py

  2. Créez un fichier env.py dans /var/www/html/ avec le contenu suivant:

import os, sys
print("\n".join(f"{key}:{value}" for key,value in os.environ.items()))
  1. Que ce passe t’il quand on consulte la page curl $middleware_ip/env ?

Un intergiciel et Redis pour une petit application web

Nous allons utiliser un intergiciel et Redis une petite application permettant:

On va assigner des bases de données des rôles différentes:

Pour communiquer avec l’application, nous allons utiliser le protocole HTTP de manière standard.

Nous allons créer un intergiciel qui orchestre tout ça. Vous pouvez utiliser le langage de programmation de votre choix, mais Python est recommandé.

La création de compte

  1. Créez un fichier redis_connect.py qui propose une fonction connection_redis prend en argument un entier qui retourne un connecteur redis pour la base de donnée passée en argument. Pour ne pas l’exposer à l’extérieur, rediriger l’url redis_connect.py vers /.

  2. Créez un fichier create.py (ou create.cgi ou autre si autre langage) et redirigez l’url create vers lui. Faites en sortes que le scripte create.py:

Les commandes Redis pour manipuler les hashmap sont hset, hget, hmget, hmset et hgetall. En python, les binding sont identitiques et permettent de stocker un dictionnaire (dont les clefs valeurs sont des types simples). Pour plus de détails, voir la documentation de Redis.

Connexion au compte

  1. Faite une redirection de l’url /connect.py vers /connect

  2. Créez un fichier connect.py qui:

Pour la durée limitée, on doit utiliser la commande expire de Redis

Avoir des informations sur le compte

  1. Faites une redirection de l’url / vers /info.py

  2. Créez un fichier info.py qui va:

API d’upload et de download de fichier

Faites une redirection de toutes les URL de la forme /files/\* vers /gestion_fichier.py et créez le fichier gestion_fichier.py

La gestion de fichier va être gérer par le même fichier (éventullement décomposé en sous fichier importé pour plus de clarté). La liste des fichiers de chaques utilisateur doit être maintenu à l’aide d’un type de donnée list. Vous pouvez lire la liste des commandes ici.

En fonction du type de requête HTTP on va réaliser une action différence. Les 4 requêtes à prendre en compte sont: GET, PUT, PATCH et DELETE. La requête GET va par ailleurs retourner la liste de fichiers pour les requêtes de la forme $remote_ip/files/ (c’est-à-dire, sans nom de fichiers supplémentaires).

Attention pour envoyer des fichiers avec curl, utiliser l’option --data-binary, l’option --data supprime les retour à la ligne…



Compiled the: dim. 07 janv. 2024 23:19:19 CET