waf: WAF haute performance construit sur la pile nginx-module-lua
Installation
Si vous n'avez pas encore configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.
CentOS/RHEL 7 ou Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-waf
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-waf
Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.
Ce document décrit lua-resty-waf v0.11.1 publié le 09 mai 2017.
lua-resty-waf est un WAF de proxy inverse construit en utilisant la pile OpenResty. Il utilise l'API Lua de Nginx pour analyser les informations de requête HTTP et traiter selon une structure de règles flexible. lua-resty-waf est distribué avec un ensemble de règles qui imite le ModSecurity CRS, ainsi que quelques règles personnalisées créées lors du développement et des tests initiaux, et un petit ensemble de correctifs virtuels pour les menaces émergentes. De plus, lua-resty-waf est distribué avec des outils pour traduire automatiquement les règles ModSecurity existantes, permettant aux utilisateurs d'étendre l'implémentation de lua-resty-waf sans avoir besoin d'apprendre une nouvelle syntaxe de règles.
lua-resty-waf a été initialement développé par Robert Paprocki pour sa thèse de maîtrise à l'Université des gouverneurs de l'Ouest.
./configure --with-pcre=/path/to/pcre/source --with-pcre-jit
Vous pouvez télécharger la source PCRE depuis le [site Web de PCRE](http://www.pcre.org/). Voir aussi ce [billet de blog](https://www.cryptobells.com/building-openresty-with-pcre-jit/) pour un guide étape par étape sur la construction d'OpenResty avec une bibliothèque PCRE activée JIT.
## Performance
lua-resty-waf a été conçu avec l'efficacité et l'évolutivité à l'esprit. Il tire parti du modèle de traitement asynchrone de Nginx et d'un design efficace pour traiter chaque transaction aussi rapidement que possible. Les tests de charge ont montré que les déploiements mettant en œuvre tous les ensembles de règles fournis, qui sont conçus pour imiter la logique derrière le ModSecurity CRS, traitent les transactions en environ 300 à 500 microsecondes par requête ; cela équivaut à la performance annoncée par le [WAF de Cloudflare](https://www.cloudflare.com/waf). Les tests ont été effectués sur une pile matérielle raisonnable (CPU E3-1230, 32 Go de RAM, 2 x 840 EVO en RAID 0), atteignant environ 15 000 requêtes par seconde. Voir [ce billet de blog](http://www.cryptobells.com/freewaf-a-high-performance-scalable-open-web-firewall) pour plus d'informations.
La charge de travail de lua-resty-waf est presque exclusivement limitée par le CPU. L'empreinte mémoire dans la VM Lua (excluant le stockage persistant soutenu par `lua-shared-dict`) est d'environ 2 Mo.
## make && sudo make install
Alternativement, installez via Luarocks :
### process_multipart_body
*Par défaut* vrai
Active le traitement des corps de requête multipart/form-data (lorsqu'ils sont présents), en utilisant le module `lua-resty-upload`. À l'avenir, lua-resty-waf pourrait utiliser ce traitement pour effectuer des vérifications plus strictes des corps de téléchargement ; pour l'instant, ce module effectue uniquement des vérifications minimales de validité sur le corps de la requête et ne journalisera pas un événement si le corps de la requête est invalide. Désactivez cette option si vous n'avez pas besoin de cette vérification, ou si des bogues dans le module en amont causent des problèmes avec les téléchargements HTTP.
*Exemple* :
```lua
location / {
access_by_lua_block {
-- désactiver le traitement des requêtes multipart/form-data
-- notez que le corps de la requête sera toujours envoyé à l'amont
waf:set_option("process_multipart_body", false)
}
}
req_tid_header
Par défaut : faux
Définit un en-tête HTTP X-Lua-Resty-WAF-ID dans la requête en amont, avec la valeur comme ID de transaction. Cet ID sera corrélé avec l'ID de transaction présent dans les journaux de débogage (si défini). Cela peut être utile pour le suivi des requêtes ou des fins de débogage.
Exemple :
location / {
access_by_lua_block {
waf:set_option("req_tid_header", true)
}
}
res_body_max_size
Par défaut : 1048576 (1 Mo)
Définit le seuil de longueur de contenu au-delà duquel les corps de réponse ne seront pas traités. Cette taille du corps de réponse est déterminée par l'en-tête de réponse Content-Length. Si cet en-tête n'existe pas dans la réponse, le corps de la réponse ne sera jamais traité.
Exemple :
location / {
access_by_lua_block {
-- augmenter la taille maximale de la réponse à 2 Mo
waf:set_option("res_body_max_size", 1024 * 1024 * 2)
}
}
res_body_mime_types
Par défaut : "text/plain", "text/html"
Définit les types MIME avec lesquels lua-resty-waf traitera le corps de la réponse. Cette valeur est déterminée par l'en-tête Content-Type. Si cet en-tête n'existe pas, ou si le type de réponse n'est pas dans cette liste, le corps de la réponse ne sera pas traité. Définir cette option ajoutera le type MIME donné aux valeurs par défaut existantes de text/plain et text/html.
Exemple :
location / {
access_by_lua_block {
-- les types MIME qui seront traités sont maintenant text/plain, text/html et text/json
waf:set_option("res_body_mime_types", "text/json")
}
}
Plusieurs types MIME peuvent être ajoutés en passant un tableau de types à set_option.
res_tid_header
Par défaut : faux
Définit un en-tête HTTP X-Lua-Resty-WAF-ID dans la réponse en aval, avec la valeur comme ID de transaction. Cet ID sera corrélé avec l'ID de transaction présent dans les journaux de débogage (si défini). Cela peut être utile pour le suivi des requêtes ou des fins de débogage.
Exemple :
location / {
access_by_lua_block {
waf:set_option("res_tid_header", true)
}
}
score_threshold
Par défaut : 5
Définit le seuil pour le score d'anomalie. Lorsque le seuil est atteint, lua-resty-waf refusera la requête.
Exemple :
location / {
access_by_lua_block {
waf:set_option("score_threshold", 10)
}
}
storage_backend
Par défaut : dict
Définir un moteur à utiliser pour le stockage de variables persistantes. Les options actuellement disponibles sont dict (zone de mémoire partagée ngx_lua), memcached, et redis.
Exemple :
location / {
acccess_by_lua_block {
waf:set_option("storage_backend", "memcached")
}
}
storage_keepalive
Par défaut : vrai
Activer ou désactiver le keepalive TCP pour les connexions aux hôtes de stockage persistant distants.
Exemple :
location / {
acccess_by_lua_block {
waf:set_option("storage_keepalive", false)
}
}
storage_keepalive_timeout
Par défaut : 10000
Configurer (en millisecondes) le délai d'expiration pour le pool de keepalive de cosocket pour les hôtes de stockage persistant distants.
Exemple :
location / {
acccess_by_lua_block {
waf:set_option("storage_keepalive_timeout", 30000)
}
}
storage_keepalive_pool_size
Par défaut : 100
Configurer la taille du pool pour le pool de keepalive de cosocket pour les hôtes de stockage persistant distants.
Exemple :
location / {
acccess_by_lua_block {
waf:set_option("storage_keepalive_pool_size", 50)
}
}
storage_memcached_host
Par défaut : 127.0.0.1
Définir un hôte à utiliser lors de l'utilisation de memcached comme moteur de stockage de variables persistantes.
Exemple :
location / {
acccess_by_lua_block {
waf:set_option("storage_memcached_host", "10.10.10.10")
}
}
storage_memcached_port
Par défaut : 11211
Définir un port à utiliser lors de l'utilisation de memcached comme moteur de stockage de variables persistantes.
Exemple :
location / {
acccess_by_lua_block {
waf:set_option("storage_memcached_port", 11221)
}
}
storage_redis_host
Par défaut : 127.0.0.1
Définir un hôte à utiliser lors de l'utilisation de redis comme moteur de stockage de variables persistantes.
Exemple :
location / {
acccess_by_lua_block {
waf:set_option("storage_redis_host", "10.10.10.10")
}
}
storage_redis_port
Par défaut : 6379
Définir un port à utiliser lors de l'utilisation de redis comme moteur de stockage de variables persistantes.
Exemple :
location / {
acccess_by_lua_block {
waf:set_option("storage_redis_port", 6397)
}
}
storage_zone
Par défaut : aucun
Définit le lua_shared_dict qui sera utilisé pour contenir les données de stockage persistant. Cette zone doit être définie dans le bloc http{} de la configuration.
Exemple :
http {
-- définir une zone de mémoire partagée de 64 Mo pour contenir les données de stockage persistant
lua_shared_dict persistent_storage 64m;
}
location / {
access_by_lua_block {
waf:set_option("storage_zone", "persistent_storage")
}
}
Plusieurs zones partagées peuvent être définies et utilisées, bien qu'une seule zone puisse être définie par emplacement de configuration. Si une zone devient pleine et que l'interface de dictionnaire partagé ne peut pas ajouter de clés supplémentaires, le message suivant sera enregistré dans le journal des erreurs :
Erreur lors de l'ajout de la clé au stockage persistant, augmentez la taille du lua_shared_dict
Gestion des Phases
lua-resty-waf est conçu pour fonctionner dans plusieurs phases du cycle de vie de la requête. Les règles peuvent être traitées dans les phases suivantes :
- access : Les informations de requête, telles que l'URI, les en-têtes de requête, les arguments URI et le corps de la requête sont disponibles dans cette phase.
- header_filter : Les en-têtes de réponse et le statut HTTP sont disponibles dans cette phase.
- body_filter : Le corps de réponse est disponible dans cette phase.
- log : Les journaux d'événements sont automatiquement écrits à la fin de cette phase.
Ces phases correspondent à leurs gestionnaires Lua Nginx appropriés (access_by_lua, header_filter_by_lua, body_filter_by_lua, et log_by_lua, respectivement). Notez que l'exécution de lua-resty-waf dans un gestionnaire de phase Lua qui n'est pas dans cette liste entraînera un comportement défectueux. Toutes les données disponibles dans une phase antérieure sont disponibles dans une phase ultérieure. C'est-à-dire que les données disponibles dans la phase access sont également disponibles dans les phases header_filter et body_filter, mais pas vice versa.
Ensembles de Règles Inclus
lua-resty-waf est distribué avec un certain nombre d'ensembles de règles conçus pour imiter la fonctionnalité du ModSecurity CRS. Pour référence, ces ensembles de règles sont listés ici :
- 11000_whitelist : Politique locale de liste blanche
- 20000_http_violation : Violation du protocole HTTP
- 21000_http_anomaly : Anomalies du protocole HTTP
- 35000_user_agent : Agents utilisateurs malveillants/suspects
- 40000_generic_attack : Attaques génériques
- 41000_sqli : SQLi
- 42000_xss : XSS
- 90000_custom : Règles personnalisées/correctifs virtuels
- 99000_scoring : Gestion du score d'anomalie
Définitions des Règles
lua-resty-waf analyse les définitions de règles à partir de blobs JSON stockés sur disque. Les règles sont regroupées en fonction de leur objectif et de leur gravité, définies comme un ensemble de règles. Les ensembles de règles inclus ont été créés pour imiter certaines fonctionnalités du ModSecurity CRS, en particulier les définitions de base_rules. De plus, le script inclus modsec2lua-resty-waf.pl peut être utilisé pour traduire des ensembles de règles supplémentaires ou personnalisés en un blob JSON compatible avec lua-resty-waf.
Notez qu'il existe plusieurs limitations dans le script de traduction, concernant les actions, collections et opérateurs non pris en charge. Veuillez consulter cette page wiki pour une liste à jour des incompatibilités connues.
Remarques
Pull Requests
Veuillez cibler toutes les demandes de tirage vers la branche de développement, ou une branche de fonctionnalité si le PR est un changement significatif. Les commits vers master ne devraient venir que sous forme de mises à jour de documentation ou d'autres changements qui n'ont pas d'impact sur le module lui-même (et peuvent être fusionnés proprement dans le développement).
Feuille de Route
- Ensemble de règles de correctifs virtuels élargi : Augmenter la couverture des menaces émergentes.
- Tests d'intégration/acceptation élargis : Augmenter la couverture des menaces courantes et des scénarios d'utilisation.
- Traductions de syntaxe ModSecurity élargies : Supporter plus d'opérateurs, de variables et d'actions.
- Profils d'application communs : Ensembles de règles ajustés pour les CMS/applications courants.
- Support de plusieurs cibles de journalisation socket/fichier : Nécessite probablement de forker le projet lua-resty-logger-socket.
Limitations
lua-resty-waf est en cours de développement et d'amélioration continue, et en tant que tel, peut être limité dans sa fonctionnalité et sa performance. Les limitations actuellement connues peuvent être trouvées dans le suivi des problèmes GitHub pour ce dépôt.
Voir Aussi
- Le projet OpenResty : http://openresty.org/
- Mon blog personnel pour des mises à jour et des notes sur le développement de lua-resty-waf : http://www.cryptobells.com/tag/lua-resty-waf/
GitHub
Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-waf.