Skip to main content

Nginx

CrowdSec

๐Ÿ“š Documentation ๐Ÿ’  Hub ๐Ÿ’ฌ Discourse

AppSecSupported
ModeLive & Stream
MetricsSupported
MTLSSupported
PrometheusUnsupported

A lua Remediation Component for nginx.

Enable the WAF for optimal protection

After installing the bouncer, enable the AppSec (WAF) Component to get virtual patching and defense against known CVEs, SQL injection, XSS, and other application-layer attacks.

Follow the dedicated AppSec Quickstart for Nginx/OpenResty โ€” it picks up right where this page ends.

How does it work ?โ€‹

This component leverages nginx lua's API, namely access_by_lua_block to check the IP address against the local API.

Supported features:

  • Live mode (query the local API for each request)
  • Stream mode (pull the local API for new/old decisions every X seconds)
  • Ban remediation (can ban an IP address by redirecting him or returning a custom HTML page)
  • Captcha remediation (can return a captcha)
  • Works with IPv4/IPv6
  • Support IP ranges (can apply a remediation on an IP range)
  • Application Security Component (forward request to CrowdSec Application Security Engine and block is necessary)

At the back, this component uses crowdsec lua lib.

Installationโ€‹

Dependenciesโ€‹

Install the following packages:

SH
sudo apt install nginx lua5.1 libnginx-mod-http-lua luarocks gettext-base lua-cjson
warning

If you have Unbuntu 22.xx, you may have an issue with lua module in nginx.
Look at FAQ for more information.

Using packagesโ€‹

First, setup crowdsec repositories.

SH
sudo apt install crowdsec-nginx-bouncer
info

In stream mode, the component will launch an internal timer to pull the local API at the first request made to the server.

Manual installationโ€‹

warning

CrowdSec NGINX component depends on nginx lua5.1 libnginx-mod-http-lua luarocks gettext-base.

It has been tested only on Debian/Ubuntu based distributions.

Download the latest release here

SH
tar xvzf crowdsec-nginx-bouncer.tgz
cd crowdsec-nginx-bouncer-v*/
./install.sh

Note: Don't run the script with sudo (the script already use sudo to install dependencies).

If you are on a mono-machine setup, the crowdsec-nginx-bouncer install script will register directly to the local crowdsec, so you're good to go !

โš ๏ธ the installation script will take care of dependencies for Debian/Ubuntu

non-debian based dependencies
  • libnginx-mod-http-lua : nginx lua support
  • lua5.1: Lua
  • lua-cjson: JSON parser/encoder for Lua
  • luarocks : Lua package manager
  • gettext-base: for the installation script

Enable the WAF (AppSec Component)โ€‹

For real-time WAF protection โ€” virtual patching, defense against known CVEs, SQLi, XSS, and other application-layer attacks โ€” turn on the AppSec Component after installing the bouncer.

Recommended: enable the WAF for optimal protection

Follow the AppSec Quickstart for Nginx/OpenResty to enable the WAF. It's a few copy-paste commands and picks up right where this installation ends.

The AppSec-related knobs in /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf are documented in the Configuration Reference below (all APPSEC_* entries).

Upgradeโ€‹

From packageโ€‹

SH
sudo apt-get update
sudo apt-get install crowdsec-nginx-bouncer
warning

Upgrade from v0 to v1 introduce many changes. Pick up the maintainer configuration to avoid anything breaking. Configuration migration might not be trivial.

Manual Upgradeโ€‹

If you already have crowdsec-nginx-bouncer installed, please download the latest release and run the following commands:

SH
tar xzvf crowdsec-nginx-bouncer.tgz
cd crowdsec-nginx-bouncer-v*/
sudo ./upgrade.sh
sudo systemctl restart nginx

Configurationโ€‹

Component configurationโ€‹

/etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf
SH/etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf
API_URL=<CROWDSEC_LAPI_URL>
API_KEY=<CROWDSEC_LAPI_KEY>
# bounce for all type of remediation that the remediation can receive from the local API
BOUNCING_ON_TYPE=all
# when the remediation receive an unknown remediation, fallback to this remediation
FALLBACK_REMEDIATION=ban
MODE=stream
REQUEST_TIMEOUT=1000
# exclude the bouncing on those location
EXCLUDE_LOCATION=
# process internal request (eg, rewrite)
ENABLE_INTERNAL=false
# Cache expiration in live mode, in second
CACHE_EXPIRATION=1
# Update frequency in stream mode, in second
UPDATE_FREQUENCY=10
#those apply for "ban" action
# /!\ REDIRECT_LOCATION and BAN_TEMPLATE_PATH/RET_CODE can't be used together. REDIRECT_LOCATION take priority over RET_CODE AND BAN_TEMPLATE_PATH
BAN_TEMPLATE_PATH=/var/lib/crowdsec/lua/templates/ban.html
REDIRECT_LOCATION=
RET_CODE=
#those apply for "captcha" action
#valid providers are recaptcha, hcaptcha, turnstile
CAPTCHA_PROVIDER=
# default is recaptcha to ensure backwards compatibility
# Captcha Secret Key
SECRET_KEY=
# Captcha Site key
SITE_KEY=
CAPTCHA_TEMPLATE_PATH=/var/lib/crowdsec/lua/templates/captcha.html
CAPTCHA_EXPIRATION=3600

# mTLS Configuration
USE_TLS_AUTH=false
TLS_CLIENT_CERT=
TLS_CLIENT_KEY=

## Application Security Component Configuration
APPSEC_URL=
#### default ###
APPSEC_FAILURE_ACTION=passthrough
APPSEC_CONNECT_TIMEOUT=100
APPSEC_SEND_TIMEOUT=100
APPSEC_PROCESS_TIMEOUT=1000
ALWAYS_SEND_TO_APPSEC=false
APPSEC_DROP_UNREADABLE_BODY=false
SSL_VERIFY=true
################

Any /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf.local content will take precedence over /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf. All fields don't have to be present in this .local. file.

NGINX Configurationโ€‹

The Remediation Component NGINX configuration is located in /etc/nginx/conf.d/crowdsec_nginx.conf :

/etc/nginx/conf.d/crowdsec_nginx.conf
SH/etc/nginx/conf.d/crowdsec_nginx.conf
lua_package_path '/usr/local/lua/crowdsec/?.lua;;';
lua_shared_dict crowdsec_cache 50m;
lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
init_by_lua_block {
cs = require "crowdsec"
local ok, err = cs.init("/etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf", "crowdsec-nginx-bouncer/v1.1.3")
if ok == nil then
ngx.log(ngx.ERR, "[Crowdsec] " .. err)
error()
end
ngx.log(ngx.ALERT, "[Crowdsec] Initialisation done")
}

map $server_addr $unix {
default 0;
"~unix:" 1;
}

access_by_lua_block {
local cs = require "crowdsec"
if ngx.var.unix == "1" then
ngx.log(ngx.DEBUG, "[Crowdsec] Unix socket request ignoring...")
else
cs.Allow(ngx.var.remote_addr)
end
}

init_worker_by_lua_block {
cs = require "crowdsec"
local mode = cs.get_mode()
if string.lower(mode) == "stream" then
ngx.log(ngx.INFO, "Initializing stream mode for worker " .. tostring(ngx.worker.id()))
cs.SetupStream()
end

if ngx.worker.id() == 0 then
ngx.log(ngx.INFO, "Initializing metrics for worker " .. tostring(ngx.worker.id()))
cs.SetupMetrics()
end
}

The component uses lua_shared_dict to share cache between all workers.

If you want to increase the cache size you need to change this value lua_shared_dict crowdsec_cache 50m;.

โš ๏ธ Do not rename the crowdsec_cache shared dict, else the component will not work anymore.

When using captcha remediationโ€‹

To make HTTP request in the component, you need to configure a resolver and ssl certifcates. Here is a our example.

To make secure HTTP request in the component, we need to specify a trusted certificate (lua_ssl_trusted_certificate). You can also change this with a valid one :

TEXT
  - /etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu/Gentoo)
- /etc/pki/tls/certs/ca-bundle.crt (Fedora/RHEL 6)
- /etc/ssl/ca-bundle.pem (OpenSUSE)
- /etc/pki/tls/cacert.pem (OpenELEC)
- /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (CentOS/RHEL 7)
- /etc/ssl/cert.pem (OpenBSD, Alpine)

Application Security Component Configurationโ€‹

To turn on the WAF, follow the AppSec Quickstart for Nginx/OpenResty.

The AppSec-related options in /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf:

/etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf
SH/etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf
# Mandatory - set to enable AppSec
APPSEC_URL=http://127.0.0.1:7422

# Optional
APPSEC_FAILURE_ACTION=passthrough # default
APPSEC_CONNECT_TIMEOUT=100 # default
APPSEC_SEND_TIMEOUT=100 # default
APPSEC_PROCESS_TIMEOUT=1000 # default
ALWAYS_SEND_TO_APPSEC=false # default
APPSEC_DROP_UNREADABLE_BODY=false # default
SSL_VERIFY=true # default
warning

Due to limitations in the underlying library used by the remediation component, by default, the body of any HTTP2/HTTP3 request without a Content-Length will not be analyzed. To avoid potential bypasses of the WAF, you can set the option APPSEC_DROP_UNREADABLE_BODY to true to drop any request whose body cannot be inspected.

Setup captchaโ€‹

Currently, we have support for 3 providers: recaptcha, hcaptcha or turnstile

If you want to use captcha with your Nginx, you must provide a Site key and Secret key in your component configuration. If you wish to use any other provider than recaptcha you must also provide a Captcha provider.

(recaptcha documentation).

(tunstile documentation).

(hcaptcha documentation)

Edit etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf and configure the following options:

SH
CAPTCHA_PROVIDER=
SECRET_KEY=
SITE_KEY=
CAPTCHA_TEMPLATE_PATH=/var/lib/crowdsec/lua/templates/captcha.html
CAPTCHA_EXPIRATION=3600

Restart Nginx.

You can add a decisions with a type captcha to check if it works correctly:

SH
sudo cscli decisions add -i <IP_TO_TEST> -t captcha

FAQโ€‹

Why aren't decisions applied instantlyโ€‹

In stream mode, the component will launch an internal timer to pull the local API at the first request. So the cache won't be refreshed until the first request hits the service.

Resolver and certificatesโ€‹

In order to resolve the captcha provider you need to set a resolver and a SSL certificate in your nginx configuration. If you already have a resolver set in nginx configuration, you don't need to add another one. Here is a config example, but you can change values:

/etc/nginx/conf.d/crowdsec_nginx.conf
SH/etc/nginx/conf.d/crowdsec_nginx.conf
resolver 8.8.8.8 ipv6=off;
lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;

And restart Nginx.

Ubuntu 22.xx getting lua errorโ€‹

On Ubuntu 22.xx, the default NGINX package does not include the lua module due to compatibility issues with other modules. This has been resolved in later versions of Ubuntu.

How to fix itโ€‹

You have a few options to resolve this issue:

  • Upgrade to Ubuntu 24.04 or later
    The lua module is included again in newer Ubuntu releases.

  • Use OpenResty instead of NGINX
    OpenResty is a drop-in replacement for NGINX that includes the lua module by default.

    Note: OpenResty uses slightly different service names and paths, but configuration remains compatible with standard NGINX.

  • Manually compile the lua module
    This is a more advanced approach and requires manual steps to build and install the module yourself.

Configuration Referenceโ€‹

API_KEYโ€‹

string

SH
API_KEY=<API_KEY>

CrowdSec Local API key.

Generated with sudo cscli bouncers add command.

API_URLโ€‹

string

SH
API_URL=http://<ip>:<port>

CrowdSec local API URL.

USE_TLS_AUTHโ€‹

boolean

SH
USE_TLS_AUTH=false  # default

Enable mutual TLS (mTLS) authentication for secure communication with CrowdSec Local API. When enabled, the bouncer will use client certificates for authentication instead of API keys.

TLS_CLIENT_CERTโ€‹

string (path to file)

SH
TLS_CLIENT_CERT=<path_to_cert>

Path to the client certificate file for mTLS authentication. This option is only used when USE_TLS_AUTH is set to true.

TLS_CLIENT_KEYโ€‹

string (path to file)

SH
TLS_CLIENT_KEY=<path_to_key>

Path to the client certificate's private key file for mTLS authentication. This option is only used when USE_TLS_AUTH is set to true.

BOUNCING_ON_TYPEโ€‹

all | ban | captcha

SH
BOUNCING_ON_TYPE=all

Type of remediation we want to bounce. If you choose ban only and receive a decision with captcha as remediation, the component will skip the decision.

FALLBACK_REMEDIATIONโ€‹

ban | captcha

SH
FALLBACK_REMEDIATION=ban

The fallback remediation is applied if the component receives a decision with an unknown remediation.

MODEโ€‹

stream | live

SH
MODE=stream

The default mode is live.

The component mode:

  • stream: The component will pull new/old decisions from the local API every X seconds (UPDATE_FREQUENCY parameter).
  • live: The component will query the local API for each requests (if IP is not in cache) and will store the IP in cache for X seconds (CACHE_EXPIRATION parameter).
note

The timer that pull the local API will be triggered after the first request.

REQUEST_TIMEOUTโ€‹

int

SH
REQUEST_TIMEOUT=1000

Timeout in milliseconds for the HTTP requests done by the component to query CrowdSec local API or captcha provider (for the captcha verification).

EXCLUDE_LOCATIONโ€‹

string (comma separated)

SH
EXCLUDE_LOCATION=/<path1>,/<path2>

The locations to exclude while bouncing. It is a list of location, separated by commas.

โš ๏ธ It is not recommended to put EXCLUDE_LOCATION=/.

ENABLE_INTERNALโ€‹

bool

SH
ENABLE_INTERNAL=true

Whether to process internal requests or not (after a rewrite for example).

Disabled by default.

CACHE_EXPIRATIONโ€‹

int

This option is only for the live mode.

SH
CACHE_EXPIRATION=1

The cache expiration, in second, for IPs that the remediation store in cache in live mode.

UPDATE_FREQUENCYโ€‹

int

This option is only for the stream mode.

SH
UPDATE_FREQUENCY=10

The frequency of update, in second, to pull new/old IPs from the CrowdSec local API.

REDIRECT_LOCATIONโ€‹

string

This option is only for the ban remediation.

SH
REDIRECT_LOCATION=/<path>

The location to redirect the user when there is a ban.

If it is not set, the component will return the page defined in the BAN_TEMPLATE_PATH with the RET_CODE (403 by default).

BAN_TEMPLATE_PATHโ€‹

string (path to file)

This option is only for the ban remediation.

SH
BAN_TEMPLATE_PATH=<path_to_html_template>

The path to a HTML page to return to IPs that trigger ban remediation.

By default, the HTML template is located in /var/lib/crowdsec/lua/templates/ban.html.

RET_CODEโ€‹

int

This option is only for the ban remediation.

SH
RET_CODE=403

The HTTP code to return for IPs that trigger a ban remediation. If nothing specified, it will return a 403.

CAPTCHA_PROVIDERโ€‹

recaptcha | hcaptcha | turnstile

This option is only for the captcha remediation.

SH
CAPTCHA_PROVIDER=<recaptcha
info

For backwards compatibility reasons recaptcha is the default if no value is set.

SECRET_KEYโ€‹

string

This option is only for the captcha remediation.

SH
SECRET_KEY=<captcha_secret_key>

The captcha secret key.

SITE_KEYโ€‹

string

This option is only for the captcha remediation.

SH
SITE_KEY=<captcha_site_key>

The captcha site key.

CAPTCHA_TEMPLATE_PATHโ€‹

string (path to file)

This option is only for the captcha remediation.

SH
CAPTCHA_TEMPLATE_PATH=<path_to_html_template>

The path to a captcha HTML template.

The component will try to replace {{captcha_site_key}} in the template with SITE_KEY parameter.

By default, the HTML template is located in /var/lib/crowdsec/lua/templates/captcha.html.

CAPTCHA_EXPIRATIONโ€‹

int

This option is only for the captcha remediation.

SH
CAPTCHA_EXPIRATION=3600

The time for which the captcha will be validated. After this duration, if the decision is still present in CrowdSec local API, the IPs address will get a captcha again.

CAPTCHA_RET_CODEโ€‹

int

This option is only for the captcha remediation.

SH
CAPTCHA_RET_CODE=200

Specifies the HTTP status code that should be returned to the client when a CAPTCHA challenge is required. This is especially useful when your traffic is routed through a CDN (like Cloudflare), where you may want to avoid triggering caching based on non-200 status codes. By default if no value is provided it will use 200 status code.

APPSEC_URLโ€‹

string

URL of the Application Security Component

SH
APPSEC_URL=http://127.0.0.1:7422

APPSEC_FAILURE_ACTIONโ€‹

passthrough | deny

SH
APPSEC_FAILURE_ACTION=passthrough  # default

Behavior when the AppSec Component return a 500. Can let the request passthrough or deny it.

ALWAYS_SEND_TO_APPSECโ€‹

boolean

SH
ALWAYS_SEND_TO_APPSEC=false  # default

Send the request to the AppSec Component even if there is a decision for the IP.

SSL_VERIFYโ€‹

boolean

SH
SSL_VERIFY=false  # default

Verify the AppSec Component SSL certificate validity.

APPSEC_CONNECT_TIMEOUTโ€‹

int (milliseconds)

SH
APPSEC_CONNECT_TIMEOUT=100  # default

The timeout of the connection between the Remediation Component and AppSec Component.

APPSEC_SEND_TIMEOUTโ€‹

int (milliseconds)

SH
APPSEC_SEND_TIMEOUT=100  # default

The timeout to send data from the Remediation Component to the AppSec Component.

APPSEC_PROCESS_TIMEOUTโ€‹

int (milliseconds)

SH
APPSEC_PROCESS_TIMEOUT=500  # default

The timeout to process the request from the Remediation Component to the AppSec Component.

APPSEC_DROP_UNREADABLE_BODYโ€‹

bool

SH
APPSEC_DROP_UNREADABLE_BODY=false #default

If the bouncer cannot read the request body (eg, HTTP2 without Content-Length header), drop or not the request without forwarding it to the WAF.

If set to false (the default), the request will be evaluated by the WAF without the body content. If set to true, the request will be blocked directly by nginx.

Nginx variablesโ€‹

Nginx variables can be used to adapt behaviour and or more flexible configurations:

  • ngx.var.crowdsec_disable_bouncer: set to 1, it will disable the bouncer
  • ngx.var.crowdsec_enable_bouncer: set to 1, it will disable the bouncer
  • ngx.var.crowdsec_enable_appsec: set to 1, it will enable the appsec even if it's disabled by configuration or if bouncer is disabled
  • ngx.var.crowdsec_disable_appsec: set to 1, it will disable the appsec
  • ngx.var.crowdsec_always_send_to_appsec: set 1, it will always send the request to appsec, even if a decision already exist for the ip requesting

If both ngx.var.crowdsec_disable_bouncer and ngx.var.crowdsec_enable_bouncer, or both ngx.var.crowdsec_disable_appsec and ngx.var.crowdsec_enable_appsec are set to 1, it's the disable configuration that prevails.

CrowdSec Docs
We use cookies

This site uses cookies to help us improve your experience. You can accept or decline below.