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 : 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 !

La grande migration, Épisode II

Magazine
Marque
Linux Pratique
Numéro
138
Mois de parution
juillet 2023
Spécialité(s)
Résumé

Dans l’épisode précédent [1], nous avons posé les premières briques d’une infrastructure d’auto-hébergement : vm-bhyve comme solution de virtualisation, sous FreeBSD donc, Wireguard comme gestionnaire de tunnel, une petite instance t4g.nano, 2 cœurs ARM64 et 512M de RAM chez AWS et un premier succès de déplacement de machine virtuelle hébergeant un simple serveur web d’un serveur dédié vers notre infrastructure personnelle. Nous allons voir maintenant comment migrer en douceur une machine virtuelle concentrant les services de base d’une architecture à l’autre.

La grande migration, Épisode I

Magazine
Marque
Linux Pratique
Numéro
137
Mois de parution
mai 2023
Spécialité(s)
Résumé

Il arrive parfois un jour où, ça y est, vous avez pris votre décision, le courage et l’envie sont là, c’est le moment : on va migrer. D’une ancienne technologie à une plus récente, par impératif professionnel, personnel, parce que c’est plus cohérent dans vos nouvelles coutumes, vous voyez exactement de quoi je parle, on frappe dans ses mains, on regarde le chantier, et on dit à voix haute : c’est parti.

Les derniers articles Premiums

Les derniers articles Premium

Quarkus : applications Java pour conteneurs

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

Initié par Red Hat, il y a quelques années le projet Quarkus a pris son envol et en est désormais à sa troisième version majeure. Il propose un cadre d’exécution pour une application de Java radicalement différente, où son exécution ultra optimisée en fait un parfait candidat pour le déploiement sur des conteneurs tels que ceux de Docker ou Podman. Quarkus va même encore plus loin, en permettant de transformer l’application Java en un exécutable natif ! Voici une rapide introduction, par la pratique, à cet incroyable framework, qui nous offrira l’opportunité d’illustrer également sa facilité de prise en main.

De la scytale au bit quantique : l’avenir de la cryptographie

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

Imaginez un monde où nos données seraient aussi insaisissables que le célèbre chat de Schrödinger : à la fois sécurisées et non sécurisées jusqu'à ce qu'un cryptographe quantique décide d’y jeter un œil. Cet article nous emmène dans les méandres de la cryptographie quantique, où la physique quantique n'est pas seulement une affaire de laboratoires, mais la clé d'un futur numérique très sécurisé. Entre principes quantiques mystérieux, défis techniques, et applications pratiques, nous allons découvrir comment cette technologie s'apprête à encoder nos données dans une dimension où même les meilleurs cryptographes n’y pourraient rien faire.

Les nouvelles menaces liées à l’intelligence artificielle

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

Sommes-nous proches de la singularité technologique ? Peu probable. Même si l’intelligence artificielle a fait un bond ces dernières années (elle est étudiée depuis des dizaines d’années), nous sommes loin d’en perdre le contrôle. Et pourtant, une partie de l’utilisation de l’intelligence artificielle échappe aux analystes. Eh oui ! Comme tout système, elle est utilisée par des acteurs malveillants essayant d’en tirer profit pécuniairement. Cet article met en exergue quelques-unes des applications de l’intelligence artificielle par des acteurs malveillants et décrit succinctement comment parer à leurs attaques.

Les listes de lecture

8 article(s) - ajoutée le 01/07/2020
Découvrez notre sélection d'articles pour faire vos premiers pas avec les conteneurs, apprendre à les configurer et les utiliser au quotidien.
11 article(s) - ajoutée le 02/07/2020
Si vous recherchez quels sont les outils du DevOps et comment les utiliser, cette liste est faite pour vous.
8 article(s) - ajoutée le 02/07/2020
Il est essentiel d'effectuer des sauvegardes régulières de son travail pour éviter de perdre toutes ses données bêtement. De nombreux outils sont disponibles pour nous assister dans cette tâche.
Voir les 58 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous