
WAF haute performance basé sur la stack OpenResty
lua-resty-waf - WAF haute performance construit sur la pile OpenResty
REMARQUE : lua-resty-waf est essentiellement abandonné. Ce projet avait son utilité à une époque où ModSecurity pour Nginx n'était pas une option viable ; ce n'est plus le cas. Une tentative de revitalisation du projet a eu lieu en 2020, mais je ne dispose pas des ressources nécessaires pour la mener à bien ; ce travail est partiellement achevé dans la branche redux.
lua-resty-waf est un WAF proxy inverse construit sur la pile OpenResty. Il utilise l'API Lua de Nginx pour analyser les informations des requêtes HTTP et les traiter selon une structure de règles flexible. lua-resty-waf est distribué avec un ensemble de règles qui imite le CRS de ModSecurity, 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 permettant de traduire automatiquement les règles ModSecurity existantes, ce qui permet aux utilisateurs d'étendre l'implémentation de lua-resty-waf sans avoir à apprendre une nouvelle syntaxe de règles.
lua-resty-waf a été initialement développé par Robert Paprocki pour son mémoire de master à la Western Governor's University.
lua-resty-waf nécessite plusieurs modules Lua resty tiers, bien qu'ils soient tous fournis avec lua-resty-waf et n'aient donc pas besoin d'être installés séparément. Il est recommandé d'installer lua-resty-waf sur un système exécutant le bundle logiciel OpenResty ; lua-resty-waf n'a pas été testé sur des plateformes construites à partir de paquets Nginx source et module Lua Nginx séparés.
Pour des performances optimales de compilation des expressions régulières, il est recommandé de construire Nginx/OpenResty avec une version de PCRE prenant en charge la compilation JIT. Si votre système d'exploitation ne la fournit pas, vous pouvez compiler directement une PCRE compatible JIT dans votre build Nginx/OpenResty. Pour ce faire, référencez le chemin vers les sources PCRE dans l'indicateur de configuration --with-pcre. Par exemple :```sh
Vous pouvez télécharger les sources de PCRE depuis le [site web de PCRE](http://www.pcre.org/). Voir aussi cet [article de blog](https://www.cryptobells.com/building-openresty-with-pcre-jit/) pour un guide pas à pas sur la construction d'OpenResty avec une bibliothèque PCRE compatible JIT.
## Performances
lua-resty-waf a été conçu en gardant à l'esprit l'efficacité et l'évolutivité. Il exploite le modèle de traitement asynchrone de Nginx et une conception efficace pour traiter chaque transaction le plus rapidement possible. Les tests de charge ont montré que les déploiements mettant en œuvre tous les ensembles de règles fournis, conçus pour imiter la logique du CRS ModSecurity, traitent les transactions en environ 300 à 500 microsecondes par requête ; cela correspond aux performances annoncées par [le WAF de Cloudflare](https://www.cloudflare.com/waf). Les tests ont été effectués sur une configuration matérielle raisonnable (CPU E3-1230, 32 Go de RAM, 2 x 840 EVO en RAID 0), atteignant un maximum d'environ 15 000 requêtes par seconde. Voir [cet article 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 liée au CPU. L'empreinte mémoire dans la VM Lua (hors stockage persistant adossé à `lua-shared-dict`) est d'environ 2 Mo.
## Installation
Un simple Makefile est fourni :```
# make && sudo make install
Sinon, installez via Luarocks:```
lua-resty-waf fait usage du gestionnaire de paquets [OPM](https://github.com/openresty/opm), disponible dans les distributions modernes d'OpenResty. Les outils clients OPM nécessitent que l'outil en ligne de commande `resty` soit disponible dans la variable d'environnement `PATH` de votre système.
Notez que par défaut, lua-resty-waf s'exécute en mode SIMULATE, afin d'éviter d'affecter immédiatement une application ; les utilisateurs qui souhaitent activer les actions des règles doivent explicitement définir le mode opérationnel sur ACTIVE.
## Synopsis```lua
http {
init_by_lua_block {
-- use resty.core for performance improvement, see the status note above
require "resty.core"
-- require the base module
local lua_resty_waf = require "resty.waf"
-- perform some preloading and optimization
lua_resty_waf.init()
}
server {
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- define options that will be inherited across all scopes
waf:set_option("debug", true)
waf:set_option("mode", "ACTIVE")
-- this may be desirable for low-traffic or testing sites
-- by default, event logs are not written until the buffer is full
-- for testing, flush the log buffer every 5 seconds
--
-- this is only necessary when configuring a remote TCP/UDP
-- socket server for event logs. otherwise, this is ignored
waf:set_option("event_log_periodic_flush", 5)
-- run the firewall
waf:exec()
}
header_filter_by_lua_block {
local lua_resty_waf = require "resty.waf"
-- note that options set in previous handlers (in the same scope)
-- do not need to be set again
local waf = lua_resty_waf:new()
waf:exec()
}
body_filter_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:exec()
}
log_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:exec()
}
}
}
}
Traduit et initialise un fichier de règles SecRules ModSecurity depuis le disque. Notez que cela nécessite toujours l'ajout du jeu de règles via add_ruleset (le nom de base du fichier doit être fourni comme clé).
Exemple:```lua http { init_by_lua_block { local lua_resty_waf = require "resty.waf"
-- this translates and calculates a ruleset called 'ruleset_name'
local ok, errs = pcall(function()
lua_resty_waf.load_secrules("/path/to/secrules/ruleset_name")
end)
-- errs is an array-like table
if errs then
for i = 1, #errs do
ngx.log(ngx.ERR, errs[i])
end
end
}
server {
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- in order to use the loaded ruleset, it must be added via
-- the 'add_ruleset' option
waf:set_option("add_ruleset", "ruleset_name")
}
}
}
}
De plus, `load_secrules` peut prendre un second argument optionnel sous forme de table d'options à passer à diverses fonctions de traduction. Les options suivantes sont reconnues :
* *path*: Définit un chemin du système de fichiers dans lequel rechercher les fichiers de données pour des opérateurs tels que @pmFromFile. Si aucune clé de ce type n'est définie, le répertoire de travail courant (`.`) est utilisé
* *force*: Ne pas générer d'erreur et ne pas interrompre en cas d'échec de traduction d'une variable de règle
* *loose*: Ne pas générer d'erreur et ne pas interrompre en cas d'échec de traduction d'une action de règle
* *quiet*: Ne pas générer d'erreur ni d'avertissement en cas d'échec de traduction d'une action de règle
Cette fonction peut également prendre une troisième option sous forme de table pour capturer les erreurs de traduction, afin de les traiter ultérieurement. Si cette option n'est pas présente ou n'est pas une table, les erreurs de traduction seront alors consignées dans le journal d'erreurs.
### lua-resty-waf.init()
Effectue un pré-calcul de certaines règles et ensembles de règles, en fonction de ce qui a été rendu disponible via les ensembles de règles distribués par défaut. Il est recommandé, mais pas obligatoire, d'appeler cette fonction (ne pas le faire entraînera une légère pénalité de performance). Cette fonction ne doit jamais être appelée en dehors de ce contexte.
*Exemple*:```lua
http {
init_by_lua_block {
local lua_resty_waf = require "resty.waf"
lua_resty_waf.init()
}
}
Instanciez une nouvelle instance de lua-resty-waf. Vous devez appeler cette méthode dans chaque phase du gestionnaire de requêtes dans laquelle vous souhaitez exécuter lua-resty-waf, et utiliser le résultat renvoyé pour appeler d'autres méthodes de l'objet.
Exemple:```lua location / { access_by_lua_block { local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
}
}
### lua-resty-waf:set_option()
Configure une option par périmètre.
*Exemple* :```lua
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- enable debug logging only for this scope
waf:set_option("debug", true)
}
}
Définir une variable de transaction (stockée dans la collection de variables TX) avant d'exécuter le WAF. Cela peut être utilisé pour définir des variables utilisées par des règles complexes telles que l'OWASP CRS.
Exemple :```lua location / { access_by_lua_block { local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
waf:set_var("FOO", "bar")
}
}
Notez que, comme pour toute autre règle ModSecurity, l'existence d'une variable n'entraîne aucun changement fonctionnel dans le traitement du WAF ; il incombe à l'auteur de la règle de comprendre et d'utiliser les variables `TX`.
### lua-resty-waf:sieve_rule()
Définissez une exclusion de collection pour une règle donnée.
*Exemple* :```lua
location / {
access_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
local sieves = {
{
type = "ARGS",
elts = "foo",
action = "ignore",
}
}
waf:sieve_rule("12345", sieves)
}
}
Voir la page wiki rule sieves pour des détails et des exemples d'utilisation avancés.
Exécute le moteur de règles. Par défaut, le moteur est exécuté selon la phase en cours. Une table optionnelle peut être passée, permettant aux utilisateurs de "simuler" l'exécution d'une phase différente.
Exemple:```lua location / { access_by_lua_block { local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- execute according to access phase collections and rules
waf:exec()
}
content_by_lua_block {
local lua_resty_waf = require "waf"
local waf = lua_resty_waf:new()
-- execute header_filter rules, passing in a table of additional collections
-- this assumes the 'request_headers' and 'status' Lua variables were
-- declared and initialized elsewhere
local opts = {
phase = 'header_filter',
collections = {
REQUEST_HEADERS = request_headers,
STATUS = status,
}
}
waf:exec(opts)
}
}
### lua-resty-waf:write_log_events()
Écrivez toutes les entrées de journal d'audit générées par la transaction. Ceci n'est optionnel que lorsque `exec` est appelé dans un gestionnaire `log_by_lua`.
*Exemple*:```lua
location / {
log_by_lua_block {
local lua_resty_waf = require "resty.waf"
local waf = lua_resty_waf:new()
-- write out any event log entries to the
-- configured target, if applicable
waf:write_log_events()
}
}
Défaut: aucun
Ajoute un ruleset supplémentaire à utiliser pendant le traitement. Cela permet aux utilisateurs de mettre en œuvre des rulesets personnalisés sans écraser le répertoire de règles inclus. Les rulesets supplémentaires doivent résider dans un dossier nommé "rules" qui se trouve dans le lua_package_path.
Exemple:```lua http { -- the rule file 50000.json must live at -- /path/to/extra/rulesets/rules/50000.json lua_package_path '/path/to/extra/rulesets/?.lua;;';
server {
location / {
access_by_lua_block {
waf:set_option("add_ruleset", "50000_extra_rules")
}
}
}
}
Multiple rulesets peuvent être ajoutés en passant une table de valeurs à `set_option`. Notez que les noms des rulesets sont triés avant le traitement. Les rulesets sont traités dans un ordre trié du plus bas au plus haut.
### add_ruleset_string
*Défaut*: aucun
Ajoute un ruleset supplémentaire à utiliser pendant le traitement. Cela permet aux utilisateurs d'implémenter des rulesets personnalisés sans écraser le répertoire de règles inclus. Les rulesets sont définis en ligne sous forme de chaîne Lua, sous la forme d'une structure JSON de ruleset traduit.
*Exemple*:```lua
location / {
access_by_lua_block {
waf:set_option("add_ruleset_string", "70000_extra_rules", [=[{"access":[{"action":"DENY","id":73,"operator":"REGEX","opts":{},"pattern":"foo","vars":[{"parse":{"values":1},"type":"REQUEST_ARGS"}]}],"body_filter":[],"header_filter":[]}]=])
}
}
Note que les noms des ensembles de règles sont triés avant le traitement et doivent être fournis sous forme de chaînes. Les ensembles de règles sont traités dans un ordre trié du plus bas au plus haut.
Défaut : false
Indique à lua-resty-waf de continuer à traiter la requête lorsqu'un en-tête Content-Type a été envoyé et ne figure pas dans la table allowed_content_types. Ces requêtes n'auront pas leur corps de requête traité par lua-resty-waf (la collection REQUEST_BODY sera nil). De cette manière, les utilisateurs n'ont pas besoin d'ajouter explicitement à la liste blanche tous les en-têtes Content-Type possibles qu'ils peuvent rencontrer.
Exemple :```lua location / { access_by_lua_block { waf:set_option("allow_unknown_content_types", true) } }
### allowed_content_types
*Défaut*: aucun
Définit un ou plusieurs en-têtes Content-Type qui seront autorisés, en plus des Content-Types par défaut `application/x-www-form-urlencoded` et `multipart/form-data`. Une requête dont le type de contenu correspond à l'un des `allowed_content_types` définira la collection `REQUEST_BODY` comme une chaîne unique contenant (plutôt qu'un tableau) ; une requête dont le type de contenu ne correspond à aucune de ces valeurs, ni à `application/x-www-form-urlencoded` ni à `multipart/form-data`, sera rejetée.
*Exemple*:```lua
location / {
access_by_lua_block {
-- define a single allowed Content-Type value
waf:set_option("allowed_content_types", "text/xml")
-- defines multiple allowed Content-Type values
waf:set_option("allowed_content_types", { "text/html", "text/json", "application/json" })
}
}
Défaut: false
Désactive/active la journalisation de débogage. Les instructions de journalisation de débogage sont imprimées dans error_log. Notez que la journalisation de débogage est très coûteuse et ne devrait pas être utilisée en environnement de production.
Exemple:```lua location / { access_by_lua_block { waf:set_option("debug", true) } }
### debug_log_level
*Défaut*: ngx.INFO
Définit la constante de niveau de journalisation nginx utilisée pour la journalisation de débogage.
*Exemple*:```lua
location / {
access_by_lua_block {
waf:set_option("debug_log_level", ngx.DEBUG)
}
}
Défaut: ngx.HTTP_FORBIDDEN
Définit le statut à utiliser lors du refus des requêtes.
Exemple :```lua location / { access_by_lua_block { waf:set_option("deny_status", ngx.HTTP_NOT_FOUND) } }
### disable_pcre_optimization
*Défaut*: false
Supprime les drapeaux `oj` de tous les appels `ngx.re.match`, `ngx.re.find` et `ngx.re.sub`. Cela peut être utile dans certains cas où d'anciennes bibliothèques PCRE sont utilisées, mais cela provoquera une grave dégradation des performances, son utilisation est donc fortement déconseillée ; les utilisateurs sont plutôt encouragés à compiler OpenResty avec une bibliothèque PCRE moderne, compatible JIT.
*Exemple*:```lua
location / {
access_by_lua_block {
waf:set_option("disable_pcre_optimization", true)
}
}
Remarque : Ce comportement est obsolète et sera supprimé dans les versions futures.
Défaut: true
Détermine s’il faut écrire des entrées de journal pour les correspondances de règles dans une transaction qui n’a pas été modifiée par lua-resty-waf. « Modifié » est défini comme lua-resty-waf agissant sur une règle dont l’action est ACCEPT ou DENY. Lorsque cette option n’est pas définie, lua-resty-waf journalisera les correspondances de règles même si la transaction n’a pas été modifiée. Par défaut, lua-resty-waf n’écrira des entrées de journal pour les correspondances que si la transaction a été modifiée.
Exemple:```lua location / { access_by_lua_block { waf:set_option("event_log_altered_only", false) } }
Notez que `mode` n'aura pas d'effet pour déterminer si une transaction est considérée comme modifiée. Autrement dit, si une règle avec une action `DENY` correspond, mais que lua-resty-waf fonctionne en mode `SIMULATE`, la transaction sera toujours considérée comme modifiée et les correspondances de règles seront journalisées.
### event_log_buffer_size
*Défaut*: 4096
Définit la taille seuil, en octets, du tampon utilisé pour contenir les journaux d'événements. Le tampon sera vidé lorsque ce seuil sera atteint.
*Exemple*:```lua
location / {
access_by_lua_block {
-- 8 KB event log message buffer
waf:set_option("event_log_buffer_size", 8192)
}
}
Défaut: ngx.INFO
Définit la constante de niveau de journal nginx utilisée pour la journalisation des événements.
Exemple:```lua location / { access_by_lua_block { waf:set_option("event_log_level", ngx.WARN) } }
### event_log_ngx_vars
*Défaut*: vide
Définit quelles variables supplémentaires de `ngx.var` sont placées dans l'événement de journal. C'est un moyen générique d'étendre l'alerte avec un contexte supplémentaire. Le nom de la variable sera la clé de l'entrée sous une clé `ngx` dans l'entrée de journal. Si la variable n'est pas présente en tant que variable nginx, aucun élément n'est ajouté à l'événement.
*Exemple*:```lua
location / {
access_by_lua_block {
waf:set_option("event_log_ngx_vars", "host")
waf:set_option("event_log_ngx_vars", "request_id")
}
}
L'événement résultant contient ces éléments supplémentaires :```json { "ngx": { "host": "example.com", "request_id": "373bcce584e3c18a" } }
### event_log_periodic_flush
*Défaut*: none
Définit un intervalle, en secondes, auquel le tampon du journal d'événements sera vidé périodiquement. Si aucune valeur n'est configurée, le tampon ne sera pas vidé périodiquement et ne sera vidé que lorsque le seuil `event_log_buffer_size` sera atteint. Configurez cette option pour les sites à très faible trafic qui pourraient ne recevoir aucune donnée de journal d'événements pendant une longue période, afin d'éviter que des données obsolètes ne restent dans le tampon.
*Exemple*:```lua
location / {
access_by_lua_block {
-- flush the event log buffer every 30 seconds
waf:set_option("event_log_periodic_flush", 30)
}
}
Défaut: false
Lorsqu'elle est définie sur true, les entrées du journal contiennent les arguments de la requête sous la clé uri_args.
Exemple:```lua location / { access_by_lua_block { waf:set_option("event_log_request_arguments", true) } }
### event_log_request_body
*Défaut*: false
Lorsqu'elle est définie sur true, les entrées de journal contiennent le corps de la requête sous la clé `request_body`.
*Exemple*:```lua
location / {
access_by_lua_block {
waf:set_option("event_log_request_body", true)
}
}
Défaut: false
Les en-têtes de la requête HTTP sont copiés dans l'événement de journal, sous la clé request_headers.
Exemple:```lua location / { access_by_lua_block { waf:set_option("event_log_request_headers", true) } }
L'événement résultant comporte ces éléments supplémentaires :```json
{
"request_headers": {
"accept": "*/*",
"user-agent": "curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3"
}
}
Défaut : false
Active les connexions SSL lors de la journalisation via TCP/UDP.
Exemple :```lua location / { access_by_lua_block { waf:set_option("event_log_ssl", true) } }
### event_log_ssl_sni_host
*Défaut* : aucun
Définit l'hôte SNI pour les connexions `lua-resty-logger-socket`.
*Exemple* :```lua
location / {
access_by_lua_block {
waf:set_option("event_log_ssl_sni_host", "loghost.example.com")
}
}
Défaut: false
Activez la vérification des certificats pour les connexions SSL lors de la journalisation via TCP/UDP.
Exemple:```lua location / { access_by_lua_block { waf:set_option("event_log_ssl_verify", true) } }
### event_log_socket_proto
*Défaut*: udp
Définit le protocole IP à utiliser (TCP ou UDP) lors de l'envoi des journaux d'événements via un socket distant. La même logique de mise en tampon et de vidage récurrent sera utilisée quel que soit le protocole.
*Exemple*:```lua
location / {
access_by_lua_block {
-- send logs via TCP
waf:set_option("event_log_socket_proto", "tcp")
}
}
Défaut : error
Définit la destination des journaux d'événements. lua-resty-waf prend actuellement en charge la journalisation dans le journal des erreurs, un fichier séparé sur le système de fichiers local, ou un serveur TCP ou UDP distant. Dans ces deux derniers cas, les journaux d'événements sont mis en mémoire tampon et vidés lorsqu'un seuil défini est atteint (voir ci-dessous pour d'autres options concernant les options de journalisation des événements).
Exemple :```lua location / { access_by_lua_block { -- send event logs to the server's error_log location (default) waf:set_option("event_log_target", "error")
-- send event logs to a local file on disk
waf:set_option("event_log_target", "file")
-- send event logs to a remote server
waf:set_option("event_log_target", "socket")
}
}
Note that, due to a limitation in the logging library used, only a single target socket can be defined. This is to say, you may only configure one `socket` target with a specific host/port combination; if you configure a second host/port combination, data will not be properly logged.
### event_log_target_host
*Défaut* : aucun
Définit le serveur cible pour les journaux d'événements qui visent un serveur distant.
*Exemple* :```lua
location / {
access_by_lua_block {
waf:set_option("event_log_target_host", "10.10.10.10")
}
}
Défaut: none
Définit le chemin cible pour les journaux d'événements qui ciblent un emplacement du système de fichiers local.
Exemple:```lua location / { access_by_lua_block { waf:set_option("event_log_target_path", "/var/log/lua-resty-waf/event.log") } }
Ce chemin doit se trouver dans un emplacement accessible en écriture par l'utilisateur nginx. Notez que, par nature, la journalisation sur disque peut entraîner une dégradation significative des performances dans les environnements à forte concurrence.
### event_log_target_port
*Défaut*: none
Définit le port cible pour les journaux d'événements qui ciblent un serveur distant.
*Exemple*:```lua
location / {
access_by_lua_block {
waf:set_option("event_log_target_port", 9001)
}
}
Défaut: none
Remplace la fonctionnalité des actions prises lorsqu'une règle correspond. Voir l'exemple pour plus de détails
Exemple:```lua
location / {
access_by_lua_block {
local deny_override = function(waf, ctx)
ngx.log(ngx.INFO, "Overriding DENY action")
ngx.status = 404
end
-- override the DENY action with the function defined above
waf:set_option("hook_action", "DENY", deny_override)
}
}
### ignore_rule
*Défaut*: none
Indique au module d'ignorer un ID de règle spécifié. Notez qu'ignorer une règle dans une chaîne entraînera l'ignorance de toute la chaîne, et le traitement continuera avec la règle suivante après la chaîne.
*Exemple*:```lua
location / {
access_by_lua_block {
waf:set_option("ignore_rule", 40294)
waf:set_option("ignore_rule", {40002, 41036})
}
}
Multiple rules can be ignored by passing a table of rule IDs to set_option.
Default: none
Instructs the module to ignore an entire ruleset. This can be useful when some rulesets (such as the SQLi or XSS CRS rulesets) are too prone to false positives, or aren't applicable to your application.
Example:```lua location / { access_by_lua_block { waf:set_option("ignore_ruleset", "41000_sqli") } }
### mode
*Défaut*: SIMULATE
Définit le mode opérationnel du module. Les options sont ACTIVE, INACTIVE et SIMULATE. En mode ACTIVE, les correspondances de règles sont journalisées et les actions sont exécutées. En mode SIMULATE, lua-resty-waf parcourt chaque règle activée et journalise les correspondances, mais n'effectue pas l'action spécifiée lors d'une exécution donnée. Le mode INACTIVE empêche le module de s'exécuter.
Par défaut, SIMULATE est sélectionné si un mode n'est pas explicitement défini ; cela oblige les nouveaux utilisateurs à implémenter activement le blocage en définissant le mode sur ACTIVE.
*Exemple*:```lua
location / {
access_by_lua_block {
waf:set_option("mode", "ACTIVE")
}
}
Défaut: aucun
Définit le(s) résolveur(s) DNS à utiliser pour les recherches RBL. Actuellement, seul le trafic UDP/53 est pris en charge. Cette option doit être définie comme une adresse numérique, pas un nom d'hôte. Si cette option n'est pas définie, toutes les règles de recherche RBL renverront false.
Exemple:```lua location / { access_by_lua_block { waf:set_option("nameservers", "10.10.10.10") } }
### process_multipart_body
*Défaut* : true
Active le traitement des corps de requêtes multipart/form-data (lorsqu'ils sont présents), en utilisant le module `lua-resty-upload`. À l'avenir, lua-resty-waf pourra utiliser ce traitement pour effectuer des vérifications plus strictes des corps d'envoi (upload) ; pour l'instant, ce module n'effectue que des vérifications de cohérence minimales sur le corps de la requête et ne journalisera pas d'é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 amont causent des problèmes avec les envois HTTP.
*Exemple* :```lua
location / {
access_by_lua_block {
-- disable processing of multipart/form-data requests
-- note that the request body will still be sent to the upstream
waf:set_option("process_multipart_body", false)
}
}
Default: false
Set an HTTP header X-Lua-Resty-WAF-ID in the upstream request, with the value as the transaction ID. This ID will correlate with the transaction ID present in the debug logs (if set). This can be useful for request tracking or debug purposes.
Example:```lua location / { access_by_lua_block { waf:set_option("req_tid_header", true) } }
### res_body_max_size
*Par défaut*: 1048576 (1 MB)
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 réponse ne sera jamais traité.
*Exemple*:```lua
location / {
access_by_lua_block {
-- increase the max response size to 2 MB
waf:set_option("res_body_max_size", 1024 * 1024 * 2)
}
}
Notez que, par nature, il est nécessaire de mettre en mémoire tampon l'intégralité du corps de la réponse afin d'utiliser correctement la réponse comme une collection. Par conséquent, augmenter considérablement ce nombre n'est pas recommandé sans justification (et sans ressources serveur suffisantes).
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 ne figure 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:```lua location / { access_by_lua_block { -- mime types that will be processed are now text/plain, text/html, and text/json waf:set_option("res_body_mime_types", "text/json") } }
Multiple MIME types can be added by passing a table of types to `set_option`.
### res_tid_header
*Défaut*: false
Définit un en-tête HTTP `X-Lua-Resty-WAF-ID` dans la réponse en aval, avec pour valeur l'ID de transaction. Cet ID correspondra à 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*:```lua
location / {
access_by_lua_block {
waf:set_option("res_tid_header", true)
}
}
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:```lua location / { access_by_lua_block { waf:set_option("score_threshold", 10) } }
### storage_backend
*Défaut* : dict
Définir un moteur à utiliser pour le stockage persistant des variables. Les options actuellement disponibles sont *dict* (zone de mémoire partagée ngx_lua), *memcached* et *redis*.
*Exemple* :```lua
location / {
acccess_by_lua_block {
waf:set_option("storage_backend", "memcached")
}
}
Défaut : true
Active ou désactive le keepalive TCP pour les connexions vers des hôtes de stockage persistant distants.
Exemple :```lua location / { acccess_by_lua_block { waf:set_option("storage_keepalive", false) } }
### storage_keepalive_timeout
*Défaut* : 10000
Configurez (en millisecondes) le délai d'expiration du pool keepalive cosocket pour les hôtes de stockage persistant distants.
*Exemple* :```lua
location / {
acccess_by_lua_block {
waf:set_option("storage_keepalive_timeout", 30000)
}
}
Défaut: 100
Configurez la taille du pool pour le pool keepalive cosocket pour les hôtes de stockage persistant à distance.
Exemple:```lua location / { acccess_by_lua_block { waf:set_option("storage_keepalive_pool_size", 50) } }
### storage_memcached_host
*Default*: 127.0.0.1
Définissez un hôte à utiliser lors de l'utilisation de memcached comme moteur de stockage de variables persistantes.
*Exemple* :```lua
location / {
acccess_by_lua_block {
waf:set_option("storage_memcached_host", "10.10.10.10")
}
}
Défaut: 11211
Définissez un port à utiliser lors de l'utilisation de memcached comme moteur de stockage persistant de variables.
Exemple:```lua location / { acccess_by_lua_block { waf:set_option("storage_memcached_port", 11221) } }
### storage_redis_host
*Default*: 127.0.0.1
Définir un hôte à utiliser lors de l'utilisation de redis comme moteur de stockage persistant de variables.
*Exemple*:```lua
location / {
acccess_by_lua_block {
waf:set_option("storage_redis_host", "10.10.10.10")
}
}
Défaut: 6379
Définissez un port à utiliser lorsque vous utilisez redis comme moteur de stockage persistant de variables.
Exemple:```lua location / { acccess_by_lua_block { waf:set_option("storage_redis_port", 6397) } }
### storage_zone
*Défaut*: none
Définit le `lua_shared_dict` qui sera utilisé pour conserver les données de stockage persistantes. Cette zone doit être définie dans le bloc `http{}` de la configuration.
*Exemple*:_```lua
http {
-- define a 64M shared memory zone to hold persistent storage data
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, la ligne suivante sera inscrite dans le journal des erreurs :
Error adding key to persistent storage, increase the size of the lua_shared_dict
lua-resty-waf est conçu pour s'exécuter dans plusieurs phases du cycle de vie d'une requête. Les règles peuvent être traitées dans les phases suivantes :
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 ne figurant pas dans cette liste entraînera un comportement défectueux. Toutes les données disponibles dans une phase antérieure sont également disponibles dans une phase ultérieure. Autrement dit, les données disponibles dans la phase access sont également disponibles dans les phases header_filter et body_filter, mais pas l'inverse.
lua-resty-waf est distribué avec un certain nombre d'ensembles de règles conçus pour imiter les fonctionnalités du CRS ModSecurity. Pour référence, ces ensembles de règles sont listés ici :
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 sévérité, définis comme un ensemble de règles. Les ensembles de règles inclus ont été créés pour imiter certaines fonctionnalités du CRS ModSecurity, en particulier les définitions 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ées 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.
Il existe un canal IRC Freenode #lua-resty-waf. Travis CI y envoie des notifications ; n'hésitez pas à poser des questions/laisser des commentaires sur ce canal également.
De plus, des questions/réponses sont disponibles sur CodeWake :
Veuillez cibler toutes les demandes de tirage vers la branche de développement, ou une branche de fonctionnalité si la demande est un changement important. Les commits sur master ne devraient se faire que sous la forme de mises à jour de documentation ou d'autres changements qui n'ont aucun impact sur le module lui-même (et peuvent être fusionnés proprement dans le développement).
lua-resty-waf est en développement et en amélioration continus et, à ce titre, ses fonctionnalités et ses performances peuvent être limitées. Les limitations actuellement connues se trouvent dans le suivi des problèmes GitHub de ce dépôt.
Ce programme est un logiciel libre : vous pouvez le redistribuer et/ou le modifier selon les termes de la GNU General Public License telle que publiée par la Free Software Foundation, soit la version 3 de la Licence, soit (à votre choix) toute version ultérieure.
Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de QUALITÉ MARCHANDE ou d'ADÉQUATION À UN USAGE PARTICULIER. Voir la GNU General Public License pour plus de détails.
Vous devriez avoir reçu une copie de la GNU General Public License avec ce programme. Sinon, voir http://www.gnu.org/licenses/
Veuillez signaler les bogues en créant un ticket avec le suivi des problèmes GitHub.