C
CrowdSec8mo ago
MTRYLA

Custom scenario not triggering neither alert nor decision

Hello ! I installed Crowdsec with helm on my Google Kubernetes Cluster with this chart version : 0.14.1 I also deployed my custom parser, custom scenario (named crowdsecurity/compte_xxx_login_error) and custom profile. When I run cscli metrics command in one of the agent pod, I find out this information : Scenario Metrics: Scenario │ Current Count │ Overflows │ Instantiated │ Poured │ Expired crowdsecurity/compte_xxx_login_error │ 1 │ 197 │ 198 │ 395 │ - What does that line mean? I guess my custom scenario was hit once. SO I don't understand why there is no triggered alert as well as no decision created by lapi. Could you help me please ? Kind regards, Mathis
56 Replies
CrowdSec
CrowdSec8mo ago
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.
© Created By WhyAydan for CrowdSec ❤️
MTRYLA
MTRYLAOP8mo ago
No description
iiamloz
iiamloz8mo ago
So it had 197 overflows, but any chance you can share the scenario file? the main part for sceanrio is:
labels:
remediation: true
labels:
remediation: true
if this is not set or false then it wont trigger from profiles unless you have a custom one
MTRYLA
MTRYLAOP8mo ago
Here are my custom parser, custom scenario and custom profile
No description
iiamloz
iiamloz8mo ago
ahhh I see the issue you are grouping on a non IP adderss metric
MTRYLA
MTRYLAOP8mo ago
yes I group on a the tls ja3 fingerprint that I parse is that a problem ?
iiamloz
iiamloz8mo ago
Not a problem but non of our remediation know about ja3 unless you have a custom one
MTRYLA
MTRYLAOP8mo ago
the http_x_client_ja3 is one of my static that I retrieve in the custom scenario
iiamloz
iiamloz8mo ago
So firstly you can add this to the sceanrio:
scope:
type: Fingerprint
expression: evt.Meta.http_x_client_ja3
scope:
type: Fingerprint
expression: evt.Meta.http_x_client_ja3
this will set the value in the alert message and scope then within the profiles you can remove the value and scope from the profile
MTRYLA
MTRYLAOP8mo ago
Like this ? type: leaky name: crowdsecurity/compte_xxx_login_error description: "Detect compte_xxx bruteforce logins" filter: evt.Meta.log_type == "compte-xxx" leakspeed: "10s" capacity: 1 groupby: evt.Meta.http_x_client_ja3 scope: type: Fingerprint expression: evt.Meta.http_x_client_ja3 labels: service: compte-xxx type: login remediation: true
iiamloz
iiamloz8mo ago
Ye
MTRYLA
MTRYLAOP8mo ago
I'm testing it seems to be working so it's gonna create one decision per alert, is that correct?
iiamloz
iiamloz8mo ago
Yeah thats correct, but remember non our remediation can handle ja3 blocking but if its alerting yeah
MTRYLA
MTRYLAOP8mo ago
sorry but I didn't get what you said
iiamloz
iiamloz8mo ago
So the alerts will be under scope "Fingerprint" but none of our remediations know how to block these types of alerts
MTRYLA
MTRYLAOP8mo ago
yes actually I stored the decisions in a postgresql database and I created a "bouncer" with a python docker image that retrieves the decisions in the database and that creates cloud armor rule depending on the values of the alert
iiamloz
iiamloz8mo ago
Awesome! then yeah your good to go then 😄
MTRYLA
MTRYLAOP8mo ago
Thank you ! Last question : how to retrieve the content of an alert ?
iiamloz
iiamloz8mo ago
Well remediations/bouncer call the LAPI on /decisions/stream https://crowdsecurity.github.io/api_doc/lapi/ which sends all decisions no matter the scope or value. this is the data struct
{
"id": 0,
"uuid": "string",
"origin": "string",
"type": "string",
"scope": "string",
"value": "string",
"duration": "string",
"until": "string",
"scenario": "string",
"simulated": true
}
{
"id": 0,
"uuid": "string",
"origin": "string",
"type": "string",
"scope": "string",
"value": "string",
"duration": "string",
"until": "string",
"scenario": "string",
"simulated": true
}
so type == Fingerprint and value will be the ja3 value
MTRYLA
MTRYLAOP8mo ago
Alright, actually the value equals to "Fingerprint", doesn't sound like I would like to
No description
iiamloz
iiamloz8mo ago
huh okay something off
MTRYLA
MTRYLAOP8mo ago
what do you mean?
iiamloz
iiamloz8mo ago
the value column should be fingerprint:<value> unless your TTY too small and it chopping if off within the sceanario if you add to the filter http_x_client_ja3 != '' does it stop working?
MTRYLA
MTRYLAOP8mo ago
I'm testing with it what does TTY stand for?
iiamloz
iiamloz8mo ago
oooh I see the isseu my bad following our friends over at F5 https://community.f5.com/kb/TechnicalArticles/ja4-part-2-detecting-and-mitigating-based-on-dynamic-ja4-reputation/328663 You need to add
name: ban_ja4_fp
filters:
- Alert.Remediation == true && Alert.GetScope() == "ja4_fp"
decisions:
- type: ban
scope: "ja4_fp"
duration: 5m
debug: true
on_success: continue
name: ban_ja4_fp
filters:
- Alert.Remediation == true && Alert.GetScope() == "ja4_fp"
decisions:
- type: ban
scope: "ja4_fp"
duration: 5m
debug: true
on_success: continue
so the scope was important so in your case
name: ban_ja3_fp
filters:
- Alert.Remediation == true && Alert.GetScope() == "Fingerprint"
decisions:
- type: ban
scope: "Fingerprint"
duration: 24h
debug: false
on_success: continue
name: ban_ja3_fp
filters:
- Alert.Remediation == true && Alert.GetScope() == "Fingerprint"
decisions:
- type: ban
scope: "Fingerprint"
duration: 24h
debug: false
on_success: continue
MTRYLA
MTRYLAOP8mo ago
Alright i'm testing the type and the duration of the decision are well retrieved but still not the value hello @iiamloz hello there 🙂
iiamloz
iiamloz8mo ago
apologise, I been off with illness the last week, can you let me know whats happening?
MTRYLA
MTRYLAOP8mo ago
no worries yes : actually I created my custom profiles and scenarios and parsers. And the value of the alert equals to "Fingerprint". The problem is that "Fingerprint" is the scope. So evt.Meta.http_x_client_ja3 seems to be empty. as I mentionned here Here are my custom profile and custom scenario: name: ban_fingerprint_24h_for_compte_xxx_fr_login_error filters: - Alert.Remediation == true && Alert.GetScenario() == "crowdsecurity/compte_xxx_fr_login_error" && Alert.GetScope() == "Fingerprint" decisions: - type: ban duration: 24h scope: "Fingerprint" on_success: continue type: leaky name: crowdsecurity/compte_xxx_fr_login_error description: "Detect compte_xxx_fr bruteforce logins" filter: evt.Meta.log_type == "compte-xxx-fr" leakspeed: "10s" capacity: 10 groupby: evt.Meta.http_x_client_ja3 scope: type: Fingerprint expression: evt.Meta.http_x_client_ja3 labels: service: compte-xxx-fr type: login remediation: true
iiamloz
iiamloz8mo ago
hmmm and the parser seems to be fine?
MTRYLA
MTRYLAOP8mo ago
yes it does :
No description
MTRYLA
MTRYLAOP8mo ago
No description
MTRYLA
MTRYLAOP8mo ago
Idk why the "Fingerprint" value is passed as the scope
iiamloz
iiamloz8mo ago
Even though it parsing it seems the expression to extract the ja3 hash is not working
MTRYLA
MTRYLAOP8mo ago
so you believe that evt.Meta.http_x_client_ja3 is empty ? Here is my parser configuration: filter: evt.Line.Labels.type == "docker" onsuccess: next_stage name: crowdsecurity/compte-xxx-fr-logs description: "Parse compte-xxx-fr logs" debug: true statics: - target: evt.StrTime expression: JsonExtract(evt.Line.Raw, "time") - meta: remote_addr expression: JsonExtractUnescape(evt.Line.Raw, "remote_addr") - meta: request_method expression: JsonExtractUnescape(evt.Line.Raw, "request_method") - meta: request_uri expression: JsonExtractUnescape(evt.Line.Raw, "request_uri") - meta: status expression: JsonExtractUnescape(evt.Line.Raw, "status") - meta: http_user_agent expression: JsonExtractUnescape(evt.Line.Raw, "http_user_agent") - meta: http_x_client_ja3 expression: JsonExtractUnescape(evt.Line.Raw, "http_x_client_ja3") - meta: log_type value: "compte-xxx-fr" what should I modify to pass the right value ? to the alert
iiamloz
iiamloz8mo ago
So I would firstly add a check to the sceanrio like evt.Meta.http_x_client_ja3 != '' to firstly check if the parser is setting the correct value as even though your metrics say "parsed" this just simply means the filter passed as there no grok it doesnt ever fail technically
MTRYLA
MTRYLAOP8mo ago
Ok when I add your filter, the alerts creation are more realistic I had the impression that at each http_x_client_ja3 value it created an alert, that's not the case anymore But when I want to trigger my scenario, it doesn't
iiamloz
iiamloz8mo ago
then the parser is not setting the value correctly, do you have the json value?
MTRYLA
MTRYLAOP8mo ago
the json value of the log ?
iiamloz
iiamloz8mo ago
yes
MTRYLA
MTRYLAOP8mo ago
Yes : {"time": "2025-02-24T13:39:08+00:00","remote_addr": "34.xx.xx.xx","remote_user": "api","host": "compte-qlf.xxx.fr","request_method": "POST","request_uri": "/auth/realms/xxx.fr/protocol/openid-connect/token","status": "200","body_bytes_sent": "1562","http_referer": "","http_user_agent": "Java/11.0.16","http_x_forwarded_for": "xx.xx.xx.xx", 34.160.223.109","http_x_client_region": "BE","http_x_client_city": "Brussels","http_x_client_ja3": "2c7b42xxxxxx","http_x_cloud_trace_context": "d1111c38a98f357779acaabbd9c8f17b/2268093701284203453","realip_remote_addr": "130.211.1.128","request_time": "0.128","request_length": "652","upstream_response_time": "0.127","upstream_response_length": "1562","upstream_header_time": "0.127","upstream_status": "200","upstream_addr": "100.92.28.160:80","upstream_connect_time": "0.000"}
iiamloz
iiamloz8mo ago
hmmm, and how are you defining the acquisition? cause the evt.Line.Labels.type == "docker" is odd cause we recommend this
labels:
type: docker
program: nginx
labels:
type: docker
program: nginx
so what is the program set too?
MTRYLA
MTRYLAOP8mo ago
It is probably set to "docker" These are the labels you recommend setting on the parser ?
iiamloz
iiamloz8mo ago
Can you share what the acquisition is set too? then I can change the parser to improve some aspects
MTRYLA
MTRYLAOP8mo ago
you mean that, isn't it? the container runtime is docker
iiamloz
iiamloz8mo ago
well when you define the values, you setup the locations of where crowdsec find the files or namespaces
MTRYLA
MTRYLAOP8mo ago
oh yes sure sorry agent: acquisition: - namespace: "keycloakx" podName: "keycloakx-" program: "keycloakx" - namespace: "keycloakx" podName: "compte-xxx-fr-" program: "compte-xxx-fr" - namespace: "gravitee" podName: "api-xxx-fr-*" program: "api-xxx-fr"
iiamloz
iiamloz8mo ago
okay, and you would say that program: "compte-xxx-fr" is what we aiming to parse?
MTRYLA
MTRYLAOP8mo ago
yes correct
iiamloz
iiamloz8mo ago
okay let me cook a new parser up
MTRYLA
MTRYLAOP8mo ago
okay thank you
iiamloz
iiamloz8mo ago
im struggling with the test data you provided @MTRYLA seems its invalid json as "http_x_forwarded_for": "xx.xx.xx.xx", 34.160.223.109" has an extra , and a quote can you DM me the raw log line?
MTRYLA
MTRYLAOP8mo ago
yes
iiamloz
iiamloz8mo ago
I grabbed it and deleted it 🙂
MTRYLA
MTRYLAOP8mo ago
thank you tell me if you find that strange
iiamloz
iiamloz8mo ago
I dm'ed you the update parser and my debugging to keep thread updated here is the updated parser
filter: "evt.Parsed.program == 'compte-xxx-fr' && UnmarshalJSON(evt.Parsed.message, evt.Unmarshaled, 'compte') in ['', nil]"
onsuccess: next_stage
name: crowdsecurity/compte-xxx-fr-logs
description: "Parse compte-xxx-fr logs"
debug: true
statics:
- meta: remote_addr
expression: evt.Unmarshaled.compte["remote_addr"]
- meta: source_ip
expression: evt.Unmarshaled.compte["remote_addr"]
- meta: request_method
expression: evt.Unmarshaled.compte["request_method"]
- meta: request_uri
expression: evt.Unmarshaled.compte["request_uri"]
- meta: status
expression: evt.Unmarshaled.compte["status"]
- meta: http_user_agent
expression: evt.Unmarshaled.compte["http_user_agent"]
- meta: http_x_client_ja3
expression: evt.Unmarshaled.compte["http_x_client_ja3"]
- meta: log_type
value: "compte-xxx-fr"
filter: "evt.Parsed.program == 'compte-xxx-fr' && UnmarshalJSON(evt.Parsed.message, evt.Unmarshaled, 'compte') in ['', nil]"
onsuccess: next_stage
name: crowdsecurity/compte-xxx-fr-logs
description: "Parse compte-xxx-fr logs"
debug: true
statics:
- meta: remote_addr
expression: evt.Unmarshaled.compte["remote_addr"]
- meta: source_ip
expression: evt.Unmarshaled.compte["remote_addr"]
- meta: request_method
expression: evt.Unmarshaled.compte["request_method"]
- meta: request_uri
expression: evt.Unmarshaled.compte["request_uri"]
- meta: status
expression: evt.Unmarshaled.compte["status"]
- meta: http_user_agent
expression: evt.Unmarshaled.compte["http_user_agent"]
- meta: http_x_client_ja3
expression: evt.Unmarshaled.compte["http_x_client_ja3"]
- meta: log_type
value: "compte-xxx-fr"
note from DM
Yes, so we use evt.Parsed.program == 'compte-xxx-fr' to filter down so we only get ones tagged as the program from the acquisition, beforehand we was getting all docker logs which is too broad
for UnmarshalJSON(evt.Line.Raw, evt.Unmarshaled, 'compte') in ['', nil] this is a helper to unmarshal the whole json string in one go rather than calling jsonextract multiple times and stores all keys on the compte key
hence why in statics we use evt.Unmarshaled.compte as the top level key then we go into each value using [] syntax
Yes, so we use evt.Parsed.program == 'compte-xxx-fr' to filter down so we only get ones tagged as the program from the acquisition, beforehand we was getting all docker logs which is too broad
for UnmarshalJSON(evt.Line.Raw, evt.Unmarshaled, 'compte') in ['', nil] this is a helper to unmarshal the whole json string in one go rather than calling jsonextract multiple times and stores all keys on the compte key
hence why in statics we use evt.Unmarshaled.compte as the top level key then we go into each value using [] syntax
to keep thread updates so another issue was found that the container_runtime was set to docker but it was containerd upon changing the value the parser had to be updated UnmarshalJSON(evt.Parsed.message, evt.Unmarshaled, 'compte') in ['', nil]" since containerd has some pre message stuff, we had to use the acutal message not the raw line classing ticket as solved, but we already spoke in DM's about the other parsers the user has filter:1==1 these are not impacting right now but could be improved in future
CrowdSec
CrowdSec8mo ago
Resolving Custom scenario not triggering neither alert nor decision This has now been resolved. If you think this is a mistake please run /unresolve

Did you find this page helpful?