I have recently encounter with an issue regarding websocket responses size. I have some rpc nodes behind haproxy (on haproxy I have crowdsec) and RPC nodes works based on websocket. The issue is some users misuse RPC nodes by sending too many requests inside one websocket connection but I have websocket response size in haproxy log and now I want to block those IPs. any idea what scenario should i write? this is my sample log.
Nov 15 13:42:45 localhost haproxy[1457596]: 65.109.180.250:43330 [15/Nov/2023:13:33:54.225] rpc_and_ws~ backend1/server1 0/0/106/106/531061 101 110226935 - - --NI 404/403/5/5/0 0/0 "GET / HTTP/1.1"
as you can see the response size is above 100mb.
Thanks
he2ss
November 16, 2023, 10:57am
2
Hi @ehsanhajian ,
Yes, you can detect this. You can choose to ban instantly the user on the first request or on multiple requests to avoid false positive following your context.
I’ll go with the instantly example, if you are using the official haproxy parser, you can have something like:
type: trigger
format: 2.0
name: ehsanhajian/rpc-dos
description: "Detect RPC dos"
filter: |
evt.Parsed.program startsWith 'haproxy' && evt.Meta.log_type == 'http_access-log' &&
Atof(evt.Parsed.bytes_read) > 100000000
groupby: "evt.Meta.source_ip"
blackhole: 2m
labels:
remediation: true
service: haproxy
I didn’t test, just to give you an example. If you want something more flexible you can use the leaky bucket
great thank you for your reply and if I want to block based on url
and userAgent
Should I add like below?
type: trigger
format: 2.0
name: ehsanhajian/rpc-dos
description: "Detect RPC dos"
filter: |
evt.Parsed.program startsWith 'haproxy' && evt.Meta.log_type == 'http_access-log' &&
Atof(evt.Parsed.bytes_read) > 100000000 && evt.Parsed.requested_url contains "MyURL" && evt.Parsed.user_agent contains "User_agent"
groupby: "evt.Meta.source_ip"
blackhole: 2m
labels:
remediation: true
service: haproxy
he2ss
November 16, 2023, 1:10pm
4
Yes, but you need to put the proper fields, evt.Parsed.requested_url
doesn’t exist you need to use evt.Parsed.http_request
.
You should look at the haproxy parser . For the user-agent, you may need to check if you have the user-agent in your logs.
good morning @he2ss . below is my scenario but It’s not working as expected. just to clarify my traffic is websocket and they have http 101 code in haproxy log. actually i want to block the IP address instantly when It’s matched with the scenario.
type: trigger
format: 2.0
name: ehsanhajian/rpc-dos
description: "Detect RPC dos"
filter: |
evt.Parsed.program startsWith 'haproxy' && evt.Meta.log_type == 'http_access-log' && Atof(evt.Parsed.bytes_read) > 5242880 && evt.Parsed.http_request contains "myurl"
groupby: "evt.Meta.source_ip"
blackhole: 2m
labels:
remediation: true
service: haproxy
he2ss
November 20, 2023, 8:45am
6
Hi @ehsanhajian ,
maybe because of the last filter part (evt.Parsed.http_request contains "myurl"
) it was an example for you. It depends if it targeted specific request, but you can remove this last part of the filter, then it will trigger on any URI.
Thanks @he2ss . It’s working after I removed evt.Parsed.http_request
. Any Idea how to add URL to the scenario?
iiAmLoz
November 23, 2023, 9:41am
8
You can use explain to list all variables so using your example above you can use these
$ cscli explain --log 'Nov 15 13:42:45 localhost haproxy[1457596]: 65.109.180.250:43330 [15/Nov/2023:13:33:54.225] rpc_and_ws~ backend1/server1 0/0/106/106/531061 101 110226935 - - --NI 404/403/5/5/0 0/0 "GET / HTTP/1.1"' --type syslog -v
line: Nov 15 13:42:45 localhost haproxy[1457596]: 65.109.180.250:43330 [15/Nov/2023:13:33:54.225] rpc_and_ws~ backend1/server1 0/0/106/106/531061 101 110226935 - - --NI 404/403/5/5/0 0/0 "GET / HTTP/1.1"
├ s00-raw
| └ 🟢 crowdsecurity/syslog-logs (+12 ~9)
| └ update evt.ExpectMode : %!s(int=0) -> 1
| └ update evt.Stage : -> s01-parse
| └ update evt.Line.Raw : -> Nov 15 13:42:45 localhost haproxy[1457596]: 65.109.180.250:43330 [15/Nov/2023:13:33:54.225] rpc_and_ws~ backend1/server1 0/0/106/106/531061 101 110226935 - - --NI 404/403/5/5/0 0/0 "GET / HTTP/1.1"
| └ update evt.Line.Src : -> /tmp/cscli_explain339870188/cscli_test_tmp.log
| └ update evt.Line.Time : 0001-01-01 00:00:00 +0000 UTC -> 2023-11-23 09:40:10.229279346 +0000 UTC
| └ create evt.Line.Labels.type : syslog
| └ update evt.Line.Process : %!s(bool=false) -> true
| └ update evt.Line.Module : -> file
| └ create evt.Parsed.timestamp8601 :
| └ create evt.Parsed.facility :
| └ create evt.Parsed.logsource : syslog
| └ create evt.Parsed.message : 65.109.180.250:43330 [15/Nov/2023:13:33:54.225] rpc_and_ws~ backend1/server1 0/0/106/106/531061 101 110226935 - - --NI 404/403/5/5/0 0/0 "GET / HTTP/1.1"
| └ create evt.Parsed.pid : 1457596
| └ create evt.Parsed.priority :
| └ create evt.Parsed.program : haproxy
| └ create evt.Parsed.timestamp : Nov 15 13:42:45
| └ update evt.Time : 0001-01-01 00:00:00 +0000 UTC -> 2023-11-23 09:40:10.229343954 +0000 UTC
| └ update evt.StrTime : -> Nov 15 13:42:45
| └ create evt.Meta.datasource_path : /tmp/cscli_explain339870188/cscli_test_tmp.log
| └ create evt.Meta.datasource_type : file
| └ create evt.Meta.machine : localhost
├ s01-parse
| ├ 🟢 crowdsecurity/haproxy-logs (+47 ~2)
| ├ update evt.Stage : s01-parse -> s02-enrich
| ├ create evt.Parsed.feconn : 403
| ├ create evt.Parsed.http_proto :
| ├ create evt.Parsed.verb : GET
| ├ create evt.Parsed.haproxy_minute : 33
| ├ create evt.Parsed.http_status_code : 101
| ├ create evt.Parsed.retries : 0
| ├ create evt.Parsed.server_name : server1
| ├ create evt.Parsed.time_backend_response : 106
| ├ create evt.Parsed.captured_request_cookie : -
| ├ create evt.Parsed.http_version : 1.1
| ├ create evt.Parsed.haproxy_month : Nov
| ├ create evt.Parsed.client_port : 43330
| ├ create evt.Parsed.srvconn : 5
| ├ create evt.Parsed.http_host :
| ├ create evt.Parsed.http_verb : GET
| ├ create evt.Parsed.bytes_read : 110226935
| ├ create evt.Parsed.time_duration : 531061
| ├ create evt.Parsed.accept_date : 15/Nov/2023:13:33:54.225
| ├ create evt.Parsed.client_ip : 65.109.180.250
| ├ create evt.Parsed.haproxy_monthday : 15
| ├ create evt.Parsed.srv_queue : 0
| ├ create evt.Parsed.time_queue : 0
| ├ create evt.Parsed.time_backend_connect : 106
| ├ create evt.Parsed.termination_state : --NI
| ├ create evt.Parsed.captured_response_cookie : -
| ├ create evt.Parsed.port :
| ├ create evt.Parsed.frontend_name : rpc_and_ws~
| ├ create evt.Parsed.haproxy_time : 13:33:54.2
| ├ create evt.Parsed.actconn : 404
| ├ create evt.Parsed.captured_request_headers :
| ├ create evt.Parsed.haproxy_year : 2023
| ├ create evt.Parsed.http_request : /
| ├ create evt.Parsed.backend_queue : 0
| ├ create evt.Parsed.beconn : 5
| ├ create evt.Parsed.haproxy_hour : 13
| ├ create evt.Parsed.time_request : 0
| ├ create evt.Parsed.request : /
| ├ create evt.Parsed.http_user :
| ├ create evt.Parsed.backend_name : backend1
| ├ create evt.Parsed.captured_response_headers :
| ├ create evt.Parsed.haproxy_milliseconds : 5
| ├ create evt.Parsed.haproxy_second : 54.2
| ├ update evt.StrTime : Nov 15 13:42:45 -> 15/Nov/2023:13:33:54 -0000
| ├ create evt.Meta.http_path : /
| ├ create evt.Meta.http_status : 101
| ├ create evt.Meta.log_type : http_access-log
| ├ create evt.Meta.service : http
| ├ create evt.Meta.source_ip : 65.109.180.250
├ s02-enrich
| ├ 🟢 crowdsecurity/dateparse-enrich (+2 ~2)
| ├ create evt.Enriched.MarshaledTime : 2023-11-15T13:33:54Z
| ├ update evt.Time : 2023-11-23 09:40:10.229343954 +0000 UTC -> 2023-11-15 13:33:54 +0000 UTC
| ├ update evt.MarshaledTime : -> 2023-11-15T13:33:54Z
| ├ create evt.Meta.timestamp : 2023-11-15T13:33:54Z
| ├ 🟢 crowdsecurity/geoip-enrich (+13)
| ├ create evt.Enriched.ASNumber : 24940
| ├ create evt.Enriched.IsInEU : true
| ├ create evt.Enriched.Latitude : 60.179700
| ├ create evt.Enriched.Longitude : 24.934400
| ├ create evt.Enriched.SourceRange : 65.108.0.0/15
| ├ create evt.Enriched.ASNNumber : 24940
| ├ create evt.Enriched.ASNOrg : Hetzner Online GmbH
| ├ create evt.Enriched.IsoCode : FI
| ├ create evt.Meta.ASNNumber : 24940
| ├ create evt.Meta.IsInEU : true
| ├ create evt.Meta.IsoCode : FI
| ├ create evt.Meta.ASNOrg : Hetzner Online GmbH
| ├ create evt.Meta.SourceRange : 65.108.0.0/15
| ├ 🟢 crowdsecurity/http-logs (+6)
| ├ create evt.Parsed.file_dir : /
| ├ create evt.Parsed.file_frag :
| ├ create evt.Parsed.impact_completion : true
| ├ create evt.Parsed.static_ressource : false
| ├ create evt.Parsed.file_ext :
| ├ create evt.Meta.http_args_len : 0
| ├ 🟢 crowdsecurity/jellyfin-whitelist (unchanged)
| └ 🟢 crowdsecurity/nextcloud-whitelist (unchanged)
├-------- parser success 🟢
├ Scenarios
└ 🟢 crowdsecurity/http-crawl-non_statics
So from above you have evt.Parsed.http_request
or evt.Parsed.request
or evt.Meta.http_path
iiAmLoz:
--type syslog -v
many thanks. the command was very helpful