#nftables IP ban based on appsec?

1 messages ยท Page 1 of 1 (latest)

celest anvil
#

I've tried to search for support threads here on discord, but can't really find anything. I've also tried to read the docs and asked chatgpt (increadiably useless, but was worth a try) but I feel like I'm missing something fundamental here.

What I want:

  • AppSec triggered from traefik to add an IP block on nftables.

My setup:

  • Ubuntu server
  • nftables in front of a traefik instance
  • crowdsec installed, listening on port 8080, appsec running listening on port 7422
  • crowdsec-bouncer-traefik-plugin installed and enabled (crowdsecMode: appsec)
  • crowdsec-firewall-bouncer

When I manually add a decision sudo cscli decisions add -i <client-up> -t ban -d 1m I'm locked out directly

When I run "curl -vk "https://<server domain>/?id=%27%20OR%201%3D1--" I can see the following in my traefik logs.

DEBUG: CrowdsecBouncerTraefikPlugin: 2025/09/21 12:25:52 ServeHTTP ip:<client computer> isTrusted:false
DEBUG: CrowdsecBouncerTraefikPlugin: 2025/09/21 12:25:52 handleNextServeHTTP ip:<client computer> isWaf:true appsecQuery statusCode:403
10.0.0.169 - - [21/Sep/2025:12:25:52 +0000] "GET /?id=%27%20OR%201%3D1-- HTTP/2.0" 403 0 "-" "curl/8.11.1" 61 "next-router@file" "-" 67ms

cscli alert list shows:

โ•ญโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ ID โ”‚     value     โ”‚                         reason                        โ”‚ country โ”‚ as โ”‚ decisions โ”‚      created_at      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 51 โ”‚ Ip:<client computer> โ”‚ anomaly score block: sql_injection: 10, anomaly: 10,  โ”‚         โ”‚    โ”‚           โ”‚ 2025-09-21T12:25:52Z

So AppSec seems to do what I want. But decisions is empty.

My understanding of this is that there's an alert created based on the AppSec trigger, but no decision is matched. SO nftables will not block anything.

worthy echoBOT
#
Important Information

Thank you for getting in touch with your support request. To expedite a swift resolution, could you kindly provide the following information? Rest assured, we will respond promptly, and we greatly appreciate your patience. While you wait, please check the links below to see if this issue has been previously addressed. If you have managed to resolve it, please use run the command /resolve or press the green resolve button below.

Log Files

If you possess any log files that you believe could be beneficial, please include them at this time. By default, CrowdSec logs to /var/log/, where you will discover a corresponding log file for each component.

Guide Followed (CrowdSec Official)

If you have diligently followed one of our guides and hit a roadblock, please share the guide with us. This will help us assess if any adjustments are necessary to assist you further.

Screenshots

Please forward any screenshots depicting errors you encounter. Your visuals will provide us with a clear view of the issues you are facing.

celest anvil
#

My undestanding of this is that the appsec.yaml controls what appsec_configs files are loaded. My config should load the custom/appsec-hybrid file. This should trigger an outofband alert when I trigger crowdsecurity/appsec-generic-test, i.e. curl <server-domain>/crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnl. This in turn should trigger the scenarios/custom-appsec.yaml file that should trigger the nftable bouncer if the filter matches. But nothing of this seems to happen.

Is there any obvious misunderstandings I've made here? Am I just completely wrong here? If so, where can I read to a better understanding of this? I feel like I must be missing something truly obvious in the docs.

(Really sorry for the huge post. Not sure how to cut it down or if I'm missing something I should include)

#

After this I tried adding a scenarios/custom-appsec.yaml file looking something like this below. (I've played around with the filter a lot-)

type: leaky
filter: evt.Parsed.source == 'crowdsec-appsec'
name: crowdsecurity/crowdsec-appsec-outofband
blackhole: 2m
leakspeed: 30s
capacity: 1
labels:
  type: exploit
  remediation: true

As well as a appsec-configs/custom-appsec.yaml file. I've played aruond with what inbound and outofband rules I have.

name: custom/appsec-hybrid
default_remediation: ban
log_level: debug

inband_rules:
  - crowdsecurity/crs
  - crowdsecurity/base-config
  - crowdsecurity/vpatch-*
  - crowdsecurity/generic-*

outofband_rules:
  - crowdsecurity/appsec-generic-test
  #  - crowdsecurity/experimental-*

And lastly my appsec.yaml file:

appsec_configs:
  - custom/appsec-hybrid
labels:
  type: appsec
  log_type: appsec-block
listen_addr: 127.0.0.1:7422
source: appsecf
#

(also sorry for the jumbled post. I was over the 2000 char limit)

celest anvil
#

Not sure if it helps at all. But this is a big of my debug log that seems to show what's going on, and where it fails. I'm just not sure why it fails.

time="2025-09-21T21:36:15+02:00" level=debug msg="custom rule for event, setting name: crowdsecurity/appsec-generic-test, version: 0.3, hash: 1ffdb3af4b6e18985b8d457e6484e749bb04629c8e73fa6df656db74898cfb9b" runner_uuid=612b69bb-c322-49d7-a6d3-c28ff9c0fcea type=appsec
time="2025-09-21T21:36:15+02:00" level=debug msg="outband rules matched : 3952763708" runner_uuid=612b69bb-c322-49d7-a6d3-c28ff9c0fcea type=appsec
time="2025-09-21T21:36:15+02:00" level=debug msg="Transaction finished" band=outband chain_rule_id=749632433 is_interrupted=true rule_id=3952763708 runner_uuid=612b69bb-c322-49d7-a6d3-c28ff9c0fcea status=403 tx_id=85b0d987-1c7a-4a9e-b2d4-14e4d2fb41c9 type=appsec variable=REQUEST_FILENAME
time="2025-09-21T21:36:15+02:00" level=debug msg="Event leaving node : ko (failed filter)" id=throbbing-dust name=crowdsecurity/syslog-logs stage=s00-raw
time="2025-09-21T21:36:15+02:00" level=debug msg="+ Processing 4 statics" id=wispy-sun name=crowdsecurity/non-syslog stage=s00-raw
time="2025-09-21T21:36:15+02:00" level=debug msg=".Parsed[message] = 'dummy-appsec-data'" id=wispy-sun name=crowdsecurity/non-syslog stage=s00-raw
time="2025-09-21T21:36:15+02:00" level=debug msg=".Parsed[program] = 'appsec'" id=wispy-sun name=crowdsecurity/non-syslog stage=s00-raw
time="2025-09-21T21:36:15+02:00" level=debug msg=".Meta[datasource_path] = 'appsec'" id=wispy-sun name=crowdsecurity/non-syslog stage=s00-raw
time="2025-09-21T21:36:15+02:00" level=debug msg=".Meta[datasource_type] = 'appsec'" id=wispy-sun name=crowdsecurity/non-syslog stage=s00-raw
#
time="2025-09-21T21:36:15+02:00" level=debug msg="Event leaving node : ok" id=wispy-sun name=crowdsecurity/non-syslog stage=s00-raw
time="2025-09-21T21:36:15+02:00" level=debug msg="move Event from stage s00-raw to s01-parse" id=wispy-sun name=crowdsecurity/non-syslog stage=s00-raw
time="2025-09-21T21:36:15+02:00" level=debug msg="node successful, stop end stage s00-raw" node-name=wispy-sun stage=s00-raw
time="2025-09-21T21:36:15+02:00" level=debug msg="Event leaving node : ko (failed filter)" id=old-cherry name=crowdsecurity/sshd-logs stage=s01-parse
time="2025-09-21T21:36:15+02:00" level=debug msg="Log didn't finish stage s01-parse"
#

It seems like it's not running my scenario at all. But not sure why

rustic pulsar
#

This would be unlikely just make sure if you run cscli parsers list that you have the appsec parser so it does to the scenarios.

celest anvil
#

And I just got it working. I missed your response here ๐Ÿ™‚ But YES. it was indeed a missed parser. I have no idea how I missed that ๐Ÿ˜

#

The last logs I pasted made me realize I never had any logs from the scenario I was expecting, just the non-syslog once. That made me read up on parsers and realized I actually didn't have an appsec parser. Installed it, started seeing logs, tweaked my filter to something working again and I was locked out of my server ๐Ÿ˜„ Success

#

This was a some what frustrating debug session, but I learnt a lot

#

Didn't originally know what I was supposed to see in the logs, making it much harder

#

Time to deploy this thing and get some real world logs I Think ๐Ÿ˜„