WIUNIX ACADEMY
CrowdSec and Nginx: trustworthy logs, private LAPI and reversible IP bans
Page 3 of 5. Review the earlier steps and prerequisites before continuing.
YOUR SYSTEM. YOUR STEPS.
Where are you working?
Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.
USE YOUR OWN VALUES
Configure this tutorial
Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.
Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.
Set values for my environment7 editable values
LET’S BUILD
Install and inspect the Nginx collection
The Hub index and collection fetch public detection content; they do not enroll an engine in Console or register with CAPI. Inspect parsers and scenarios and record the installed revisions. The nginx label must match the conventional combined access-log grammar configured below. Acquisition merely reads lines; parsers normalize them, scenarios detect patterns, profiles turn eligible alerts into decisions.
The raw archive has no pre-enabled Linux base collection. Explicit syslog-logs supplies the non-syslog input stage and dateparse-enrich supplies timestamps; install both before the Nginx collection. Ignore generic systemctl reload suggestions printed by Hub commands for this private foreground lab.
Dedicated disposable VM · terminal
set -eu
sudo '/srv/academy-crowdsec/crowdsec-v1.8.1/cmd/crowdsec-cli/cscli' -c '/srv/academy-crowdsec/config/engine.yaml' hub update
sudo '/srv/academy-crowdsec/crowdsec-v1.8.1/cmd/crowdsec-cli/cscli' -c '/srv/academy-crowdsec/config/engine.yaml' parsers install crowdsecurity/syslog-logs crowdsecurity/dateparse-enrich
sudo '/srv/academy-crowdsec/crowdsec-v1.8.1/cmd/crowdsec-cli/cscli' -c '/srv/academy-crowdsec/config/engine.yaml' collections install crowdsecurity/nginx
sudo '/srv/academy-crowdsec/crowdsec-v1.8.1/cmd/crowdsec-cli/cscli' -c '/srv/academy-crowdsec/config/engine.yaml' collections inspect crowdsecurity/nginx --no-metrics
The Nginx collection and dependencies are installed and inspect lists actual versions.
LET’S BUILD
Register without printing or passing keys in argv
The Python process captures the generated key in memory and writes a new0600 private file; do not enable shell tracing, print the file, paste it into this article or commit it. No key is supplied on a command line. Machine auto-registration writes its password into the new private credential path. If any stage fails, inspect only non-secret errors; do not overwrite the partial private files. Use a fresh lab or revoke the dedicated identity before a reviewed retry.
Dedicated disposable VM · terminal
sudo python3 - <<'PY'
import os,subprocess
from pathlib import Path
r=Path('/srv/academy-crowdsec')
cli=[str(r/'crowdsec-v1.8.1/cmd/crowdsec-cli/cscli'),'-c',str(r/'config/engine.yaml')]
creds=r/'config/local-api.yaml'
assert not creds.exists() and not creds.is_symlink(), 'Credentials already present'
subprocess.run(cli+['machines','add','academy-agent','--auto','--file',str(creds),'--url','http://127.0.0.1:18081'],check=True,stdout=subprocess.DEVNULL)
os.chmod(creds,0o600)
p=r/'config/bouncer.conf'
fd=os.open(p,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
try:
key=subprocess.check_output(cli+['bouncers','add','academy_nginx','-o','raw'],text=True).strip()
assert key and '\n' not in key
text='API_URL=http://127.0.0.1:18081\nAPI_KEY='+key+'\nMODE=live\nCACHE_EXPIRATION=1\nREQUEST_TIMEOUT=500\nBOUNCING_ON_TYPE=ban\nFALLBACK_REMEDIATION=ban\nRET_CODE=403\nAPPSEC_URL=\n'
with os.fdopen(fd,'w',encoding='utf-8') as f: f.write(text)
finally:
key=None
print('Private machine credentials and exclusive bouncer file created; no key displayed')
PY
Only a non-secret confirmation appears; bouncer.conf and local-api.yaml are0600.
LET’S BUILD
Configure a separate Nginx process
Edit the reserved configuration with the following file. No real-IP module directives are enabled: the TCP peer supplies both log identity and bouncer identity. Forwarded headers are ignored. The access-phase Lua hook must be reached; a rewrite-phase return200 would bypass it, so this lab serves a real static file through try_files.
Dedicated disposable VM · terminal
sudo nano '/srv/academy-crowdsec/config/nginx.conf'
Only the dedicated configuration is edited; no global conf.d include is added.
Nginx Lua access hook and conventional logs
File content. The distro module include loads the matching NDK/Lua modules. The private key is read by the root master at initialization; workers run as www-data and need no access to the private config directory.
load_module /usr/lib/nginx/modules/ndk_http_module.so;
load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;
user www-data;
worker_processes 1;
pid /srv/academy-crowdsec/run/nginx.pid;
error_log /srv/academy-crowdsec/logs/nginx-error.log warn;
events { worker_connections 128; }
http {
lua_package_path '/srv/academy-crowdsec/crowdsec-nginx-bouncer-v1.2.3/lua-mod/lib/?.lua;/srv/academy-crowdsec/rocks/share/lua/5.1/?.lua;;';
lua_shared_dict crowdsec_cache 10m;
init_by_lua_block {
cs = require "crowdsec"
local ok, err = cs.init("/srv/academy-crowdsec/config/bouncer.conf", "crowdsec-nginx-bouncer/v1.2.3")
if ok == nil then error(err) end
}
log_format academy '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
access_log /srv/academy-crowdsec/logs/access.log academy;
server {
listen 127.0.0.1:18080;
server_name localhost;
root /srv/academy-crowdsec/www;
location / {
access_by_lua_block { cs.Allow(ngx.var.remote_addr) }
try_files $uri =404;
}
}
}
LET’S BUILD
Validate and start the foreground engine
Use terminalA. The last command stays running; leave it open. Validation checks syntax, not the complete detection/enforcement chain. No online credentials exist, and the engine is explicitly started without CAPI.
Dedicated disposable VM · terminal
sudo '/srv/academy-crowdsec/crowdsec-v1.8.1/cmd/crowdsec/crowdsec' -c '/srv/academy-crowdsec/config/engine.yaml' -t -no-capi
sudo nginx -t -p '/srv/academy-crowdsec/' -c '/srv/academy-crowdsec/config/nginx.conf'
sudo '/srv/academy-crowdsec/crowdsec-v1.8.1/cmd/crowdsec/crowdsec' -c '/srv/academy-crowdsec/config/engine.yaml' -no-capi
Both configuration tests succeed; terminalA runs the local engine and API.
LET’S BUILD
Allow trusted management, then start the lab web server
In terminalB create the named management allowlist before enforcement testing. The allowlist skips future matching decisions; review existing decisions separately rather than assuming they disappeared. Do not allowlist entire customer NATs. The test source 127.0.0.2 is deliberately outside 127.0.0.1/32.
Dedicated disposable VM · terminal
sudo '/srv/academy-crowdsec/crowdsec-v1.8.1/cmd/crowdsec-cli/cscli' -c '/srv/academy-crowdsec/config/engine.yaml' allowlists create academy-management -d 'Trusted lab management only'
sudo '/srv/academy-crowdsec/crowdsec-v1.8.1/cmd/crowdsec-cli/cscli' -c '/srv/academy-crowdsec/config/engine.yaml' allowlists add academy-management '127.0.0.1/32'
sudo '/srv/academy-crowdsec/crowdsec-v1.8.1/cmd/crowdsec-cli/cscli' -c '/srv/academy-crowdsec/config/engine.yaml' allowlists inspect academy-management
sudo nginx -p '/srv/academy-crowdsec/' -c '/srv/academy-crowdsec/config/nginx.conf'
curl --noproxy '*' --interface '127.0.0.2' --fail http://127.0.0.1:18080/index.html
Allowlist contains the reviewed CIDR; local HTTP returns academy-ok.