Demandez la Lune à nginx !

Magazine
Marque
GNU/Linux Magazine
Numéro
172
Mois de parution
juin 2014
Spécialité(s)


Résumé
Nginx n'est plus un logiciel à la marge, avec près de 20% de parts de marché du top un million des sites les plus fréquentés au monde, il représente même un acteur sur lequel on peut (et doit !) compter.

Body

La souplesse de nginx, sa configuration aux antipodes de l'ancestral apache.conf, mais aussi et surtout ses incroyables performances en font un des « chouchous » des acteurs du Web aujourd'hui.

Nginx dispose d'une palette de modules officiels des plus utiles, mais il est souvent intéressant de s'enquérir des contributions tierces, car certaines d'entre elles amènent parfois de véritables révolutions. Nous avions déjà parcouru dans ces colonnes les capacités du pare-feu applicatif NAXSI [1], nous allons aujourd'hui plonger dans les fabuleuses possibilités amenées par le module lua.

1. Programmer le protocole

Imaginez une seconde : qui, dans la chaîne HTTP, est le mieux placé pour tout voir passer, interagir, modifier ? Le serveur lui-même, voire le proxy inverse [2], rôle que joue avec brio nginx. Aussi, à l'aide de directives nginx et une API Lua [3] très accessible, on pourra, à la volée :

- Transformer les URI,

- Manipuler les en-têtes,

- Générer du contenu,

- Interagir avec d'autres composants,

- Intervenir dans le corps des objets transférés.

Le tout avec un véritable langage de programmation dont l'empreinte est minuscule et les performances « proches du C natif », en particulier grâce au compilateur LuaJIT [4].

En effet, afin d'obtenir les meilleures performances, il est fortement recommandé par le site du projet d'utiliser LuaJIT, le compilateur Just In Time, plutôt que l'interpréteur Lua standard dès que cela est possible. Il est intéressant de noter que l'interpréteur de LuaJIT, à l'occasion de sa version 2.0, a été réécrit en assembleur. On est loin d'autres univers moins regardants...

2. Installation

Si vous êtes l'heureux possesseur d'un système d'exploitation utilisant pkgsrc [5] comme système de paquets, la tâche sera relativement aisée puisque votre serviteur a inclus le support LuaJIT dans les paquets www/nginx et www/nginx-devel en mars 2014. Aussi, il vous suffira d'ajouter au fichier /etc/mk.conf la directive de compilation suivante :

PKG_OPTIONS.nginx+= luajit

Ou encore d'installer le paquet binaire proposé par NetBSDfr [6], qui l'inclura au moment où vous lirez ces lignes.

Utilisateurs de Debian GNU/Linux, si le paquet nginx-extras [7] inclut bien le support Lua, il ne s'agit malheureusement que de l'interpréteur standard et non de la version Jit. Cela permettra tout de même de s'essayer aux exemples que nous allons dérouler dans cet article, mais en production, cela vous priverait de performances optimales.

La compilation manuelle n'est pas beaucoup plus fastidieuse grâce au projet OpenResty [8] :

$ VERSION=1.5.8.1 # à remplacer par la version courante d’OpenResty
$ wget http://openresty.org/download/ngx_openresty-${VERSION}.tar.gz
$ tar zxvf ngx_openresty-${VERSION}.tar.gz
$ cd ngx_openresty-${VERSION}/
$ ./configure --with-luajit
$ make
$ make install

Le préfixe utilisé sera /usr/local/openresty/nginx, ce qui évitera de semer le chaos dans votre installation.
À noter que les utilisateurs de Debian GNU/Linux et Ubuntu devront s'acquitter de l'ajout préalable des paquets suivants :

# apt-get install libreadline-dev libncurses5-dev libpcre3-dev
libssl-dev perl make

Et les utilisateurs de Fedora / Red Hat :

# yum install readline-devel pcre-devel openssl-devel

3. Bonjour, AK47

Nginx disposant désormais des super-pouvoirs que lui octroie LuaJIT, nous allons pouvoir exposer notre premier -minuscule- bout de code à l'aide du langage Lua. Nous allons pour cela déclarer une location nginx particulière, que nous agrémenterons au fil de nos expériences. Pour le moment, nous déclarons le type MIME text/plain. Voici l'extrait concerné d'un fichier nginx.conf :

location /luatest {
default_type ‘text/plain’;
content_by_lua ‘ngx.say("Bonjour, Camarade.")’;
}

Notre premier morceau de Lua / nginx. Nous utilisons ici la directive content_by_lua, qui exécutera le code placé entre simple ou double quotes. On pourrait également appeler un fichier contenant du code Lua grâce à la directive content_by_lua_file à qui on passerait en paramètre le chemin vers le script.

On utilise au sein de ce « programme » la fonction say de l'API nginx de Lua. Cette fonction affiche une chaîne de caractères et la suffixe d'un retour chariot (\n).

Nous avons bien évidemment accès, depuis un morceau de code Lua, aux différentes variables de nginx, par exemple :

content_by_lua ‘ngx.print("Document root: [" .. ngx.var["document_root"] .. "]\\n")’

Ou encore :

content_by_lua ‘ngx.print("Document root: [" .. ngx.var.document_root .. "]\\n")’

Résultat :

imil@tatooine:~$ curl -o- -s http://coruscant/luatest
Document root: [/home/imil/www]

Et puisque nous avons accès à toutes les variables internes de nginx, nous pouvons conditionner des comportements utiles à l'administrateur système en quête d'informations :

if tonumber(ngx.var.upstream_response_time) > 1 then
ngx.log(ngx.WARN, "[LENT!] Reponse de l’upstream <" .. ngx.var.upstream_addr ..
">: " .. ngx.var.upstream_response_time);
end

À ce propos, en cas d'erreur de programmation, cette dernière se retrouvera naturellement dans l'error.log défini dans la configuration du serveur.

Bien entendu, on a également accès aux variables que sait manipuler le serveur :

content_by_lua ‘ngx.print("glmf vaut: <" .. ngx.var["arg_glmf"] .. ">\\n")’

Résultat :

imil@tatooine:~$ curl -o- -s http://coruscant/luatest/?glmf=OVER9000
glmf vaut: <OVER9000>

Rentrons un peu plus dans les arcanes du protocole ; parmi les informations dont dispose le serveur web, on retrouve la requête émanant du client, que l'on peut afficher sans plus de soin :

content_by_lua ‘ngx.print(ngx.req.raw_header())’

Résultat :

imil@tatooine:~$ curl -o- -s http://coruscant/luatest
GET /luatest HTTP/1.1
User-Agent: curl/7.26.0
Host: coruscant
Accept: */*

Ou encore récupérer dans un tableau associatif bien plus aisé à manipuler :

content_by_lua ‘
local head = ngx.req.get_headers()
for k, v in pairs(head) do
ngx.say("header disponible: " .. k)
end
ngx.say("Host: ", head["Host"])
‘;

Résultat :

imil@tatooine:~$ curl -o- -s http://coruscant/luatest
header disponible: user-agent
header disponible: host
header disponible: accept
Host: coruscant

Avant de servir la ressource demandée, l'API lua-nginx permet également de s'interposer dans l'échange :

local res = ngx.location.capture("/foo")
if res.status ~= 200 then
ngx.print("GOULAG! (reponse: " .. res.status .. ")\\n")
else
ngx.print(res.body)
end

Résultat :

imil@tatooine:~$ curl -o- -s http://coruscant/luatest
GOULAG! (reponse: 404)

Quelques explications s'imposent ici. L'appel à ngx.location.capture, qui est non-bloquant, comme nginx lui-même, effectue une requête interne vers la location foo ; comprendre qu'il n'y a aucune transaction TCP, HTTP ou encore d'IPC. Cela permet une efficience optimale, puisque s'articulant sur les mécanismes internes à nginx. ngx.location.capture retourne dans la variable locale res le tableau suivant :

- res.status contient le code de retour de la réponse HTTP ;

- res.header est un tableau contenant l'ensemble des en-têtes de réponse ;

- res.body contient le corps du message, par exemple le HTML ;

- res.truncated est un drapeau booléen permettant de savoir si le contenu de res.body est tronqué.

Et puisque l'on dispose du contenu retourné dans res.body, on peut a priori le transformer comme bon nous semble :

location /luatest {
default_type ‘text/plain’;
content_by_lua ‘
local res = ngx.location.capture("/")
if res.status ~= 200 then
ngx.say("GOULAG!! (reponse: " .. res.status .. ")")
else
local c, n, err = ngx.re.sub(res.body, "[uU][pP]", "down")
if c then
ngx.print(c)
else
ngx.log(ngx.ERR, "erreur: ", err)
return
end
end
‘;
}

Oui, pour ne rien gâcher, l'API lua-nginx supporte les expressions régulières.

Avec cette portion de code, lorsqu'on interroge la racine de coruscant, on obtient :

imil@tatooine:~$ curl -o- -s http://coruscant/
up.

Et lorsqu'on interroge la location /luatest :

imil@tatooine:~$ curl -o- -s http://coruscant/luatest
down.

Effrayant. À utiliser avec discernement, évidemment, mais les possibilités offertes par les capacités de matching des fonctions ngx.re.* ouvrent la porte à de multiples possibilités de contrôle de validité ou d'interaction avec des modules Lua tiers, tels que resty.redis [9] ou resty.memcached [10].

4. In mother Russia, Lua controls you.

Nous nous sommes jetés tête la première sur la directive content_by_lua, mais ce n'est pas, loin s'en faut, la seule section manipulable en Lua. Voyons ensemble les possibilités offertes par quelques-unes d'entre elles.

- set_by_lua nous permettra de réaliser des opérations complexes en Lua, afin de retourner une valeur dans une variable à la disposition de nginx. Ceux d'entre vous qui ont buté sur les limitations de la manipulation des variables au sein du serveur HTTP verront ici une façon simple d'additionner, soustraire, concaténer ou encore importer des valeurs dépendant d'une infinité de paramètres.

L'utilisation est similaire à content_by_lua si ce n'est la présence d'une variable de retour.

set $tagada ‘’;
set_by_lua $ret ‘
ngx.var.tagada = "tsointsoin"
return "et hop"
‘;

Ici, la variable nginx $tagada vaut « tsointsoin » et $ret « et hop ».

- rewrite_by_lua agit dans la phase de réécriture ; on pourra par exemple utiliser cette fonctionnalité pour rediriger immédiatement vers une location interne à l'aide de la fonction ngx.exec :

rewrite_by_lua ‘
local m, err = ngx.re.match(ngx.var.remote_addr, "192\.168\.1\.")
if m then
ngx.exec("/")
end
‘;

- access_by_lua donnera une immense souplesse dans la gestion des autorisations d'accès à une ressource. Voyons un exemple basique d'authentification dépendant d'une ressource tierce :

access_by_lua ‘
local res = ngx.location.capture("/auth")
if res.status == ngx.HTTP_OK then
return -- tout s’est bien déroulé, on poursuit
end
if res.status == ngx.HTTP_FORBIDDEN then
ngx.exit(res.status) -- le backend a renvoyé un code 403,
on le relaye et on sort
end
‘;

- Terminons cette liste d'exemples par la directive header_filter_by_lua grâce à laquelle on pourra réécrire les contenus des en-têtes de façon triviale :

ngx.header["X-Powered-By"] = "nginx baby!"

On imagine facilement ici la surcharge d'en-têtes malencontreusement laissée par un administrateur peu soucieux de la sécurité de ses serveurs.

5. Bon bah j'deviendrais bien maître du monde moi ce soir

Ce n'est là qu'une poignée des quarante (à ce jour) directives à votre disposition pour ajuster à la volée le comportement souhaité entre l'utilisateur et la ressource visitée. À l'aide des nombreux modules tiers disponibles, d'autres horizons ingénieux s'offrent à vous ; on remarquera par exemple le site http://devblog.mixlr.com/2012/09/01/nginx-lua/ qui donne l'exemple d'une directive access_by_lua qui fait communiquer nginx avec Redis [11] pour lire rapidement si un visiteur fait partie des adresses IP bannies sur le serveur.

Du fait de la simplicité du langage Lua et des innombrables possibilités du combo avec le serveur nginx, gageons que les contributions et nouveautés vont pulluler d'ici peu.

Références

[1] Site du pare-feu NAXSI : https://github.com/nbs-system/naxsi

[2] Définition d'un proxy inverse : http://fr.wikipedia.org/wiki/Proxy_inverse

[3] Site officiel de Lua : http://www.lua.org/

[4] Comparaison des performances de Lua et LuaJIT sur différentes architectures : http://luajit.org/performance.html

[5] Site officiel du projet pkgsrc (portable package build system) : http://pkgsrc.org/

[6] Paquets NetBSDfr : http://packages.netbsdfr.org/

[7] Paquet nginx-extra GNU/Debian : https://packages.debian.org/fr/wheezy/nginx-extras

[8] Site officiel du projet OpenResty : http://openresty.org/

[9] Module Lua Redis : https://github.com/agentzh/lua-resty-redis

[10] Module Lua Memcached : https://github.com/agentzh/lua-resty-memcached

[11] Site officiel de Redis : http://redis.io/




Article rédigé par

Par le(s) même(s) auteur(s)

smolBSD : UNIX façon « Ikea »

Magazine
Marque
Linux Pratique
Numéro
141
Mois de parution
janvier 2024
Spécialité(s)
Résumé

Dans les séries américaines, on appelle ce genre d’histoire un crossover, les premières occurrences ont démarré dans Linux Pratique, puis une partie plus profonde sur l’amincissement d’un noyau NetBSD, pas nécessairement utile pour la finalité de notre produit, a fait son apparition dans GNU/Linux Magazine. Aujourd’hui, nous allons apprendre à construire un système BSD UNIX, NetBSD, From Scratch.

SmolBSD : un système UNIX de 7 mégaoctets qui démarre en moins d’une seconde

Magazine
Marque
GNU/Linux Magazine
Numéro
265
Mois de parution
septembre 2023
Spécialité(s)
Résumé

Que de racolage en si peu de mots. Et pourtant si, c’est bien la promesse de cet article, comment parvenir à construire un système d’exploitation fonctionnel en moins de… 10 mégabits. Quelle est cette sorcellerie ? En utilisant une fonctionnalité prévue, mais pas utilisée à cet escient par le noyau NetBSD, nous allons lui faire subir un régime drastique !

Les derniers articles Premiums

Les derniers articles Premium

La place de l’Intelligence Artificielle dans les entreprises

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’intelligence artificielle est en train de redéfinir le paysage professionnel. De l’automatisation des tâches répétitives à la cybersécurité, en passant par l’analyse des données, l’IA s’immisce dans tous les aspects de l’entreprise moderne. Toutefois, cette révolution technologique soulève des questions éthiques et sociétales, notamment sur l’avenir des emplois. Cet article se penche sur l’évolution de l’IA, ses applications variées, et les enjeux qu’elle engendre dans le monde du travail.

Petit guide d’outils open source pour le télétravail

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Ah le Covid ! Si en cette période de nombreux cas resurgissent, ce n’est rien comparé aux vagues que nous avons connues en 2020 et 2021. Ce fléau a contraint une large partie de la population à faire ce que tout le monde connaît sous le nom de télétravail. Nous avons dû changer nos habitudes et avons dû apprendre à utiliser de nombreux outils collaboratifs, de visioconférence, etc., dont tout le monde n’était pas habitué. Dans cet article, nous passons en revue quelques outils open source utiles pour le travail à la maison. En effet, pour les adeptes du costume en haut et du pyjama en bas, la communauté open source s’est démenée pour proposer des alternatives aux outils propriétaires et payants.

Sécurisez vos applications web : comment Symfony vous protège des menaces courantes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les frameworks tels que Symfony ont bouleversé le développement web en apportant une structure solide et des outils performants. Malgré ces qualités, nous pouvons découvrir d’innombrables vulnérabilités. Cet article met le doigt sur les failles de sécurité les plus fréquentes qui affectent même les environnements les plus robustes. De l’injection de requêtes à distance à l’exécution de scripts malveillants, découvrez comment ces failles peuvent mettre en péril vos applications et, surtout, comment vous en prémunir.

Bash des temps modernes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les scripts Shell, et Bash spécifiquement, demeurent un standard, de facto, de notre industrie. Ils forment un composant primordial de toute distribution Linux, mais c’est aussi un outil de prédilection pour implémenter de nombreuses tâches d’automatisation, en particulier dans le « Cloud », par eux-mêmes ou conjointement à des solutions telles que Ansible. Pour toutes ces raisons et bien d’autres encore, savoir les concevoir de manière robuste et idempotente est crucial.

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Voir les 125 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous