cscli parsers install bunkerity/bunkerweb-logsParser to ingest BunkerWeb access and error logs, extracting the optional request identifier when present.
Ensure the parser is loaded together with crowdsecurity/http-logs and crowdsecurity/dateparse-enrich.
Typical acquisition for the default file target:
1---2filenames:3 - /var/log/bunkerweb/access.log4labels:5 type: bunkerweb
The parser accepts both legacy (without $request_id) and current BunkerWeb formats:
1$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"2$host $remote_addr - $request_id $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
If you customise the format, keep the virtual host and (when enabled) the request identifier in the leading fields so the parser can extract target_fqdn and request_id.
1# Handle both legacy (without $request_id) and modern BunkerWeb access formats.2filter: "evt.Parsed.program startsWith 'bunkerweb'"3onsuccess: next_stage4name: bunkerity/bunkerweb-logs5description: "Parse BunkerWeb access and error logs, including optional request identifiers"6pattern_syntax:7 NGCUSTOMUSER: '[a-zA-Z0-9\.\@\-\+_%]+'8 NGCUSTOMURIPATH: "(?:/[A-Za-z0-9$.+!*'\\(\\)\\{\\},~:;=@\\#%&_\\-]*)+"9 NGCUSTOMURIPATHPARAM: "%{NGCUSTOMURIPATH}(?:%{URIPARAM})?"10nodes:11 - grok:12 pattern: '%{NOTSPACE:target_fqdn}(?::%{INT:port})? %{IPORHOST:remote_addr} - %{NOTSPACE:request_id} %{NGCUSTOMUSER:remote_user} \[%{HTTPDATE:time_local}\] "%{WORD:verb} %{DATA:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status} %{NUMBER:body_bytes_sent} "%{NOTDQUOTE:http_referer}" "%{NOTDQUOTE:http_user_agent}"(?: %{NUMBER:request_length} %{NUMBER:request_time} \[%{DATA:proxy_upstream_name}\] \[%{DATA:proxy_alternative_upstream_name}\])?'13 apply_on: message14 statics:15 - meta: log_type16 value: http_access-log17 - target: evt.StrTime18 expression: evt.Parsed.time_local19 - grok:20 pattern: '%{NOTSPACE:target_fqdn}(?::%{INT:port})? %{IPORHOST:remote_addr} - %{NOTSPACE:request_id} %{NGCUSTOMUSER:remote_user} \[%{HTTPDATE:time_local}\] "%{DATA:request}" %{NUMBER:status} %{NUMBER:body_bytes_sent} "%{NOTDQUOTE:http_referer}" "%{NOTDQUOTE:http_user_agent}"(?: %{NUMBER:request_length} %{NUMBER:request_time} \[%{DATA:proxy_upstream_name}\] \[%{DATA:proxy_alternative_upstream_name}\])?'21 apply_on: message22 statics:23 - meta: log_type24 value: http_access-log25 - target: evt.StrTime26 expression: evt.Parsed.time_local27 - grok:28 pattern: '%{NOTSPACE:target_fqdn}(?::%{INT:port})? %{IPORHOST:remote_addr} - %{NGCUSTOMUSER:remote_user} \[%{HTTPDATE:time_local}\] "%{WORD:verb} %{DATA:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status} %{NUMBER:body_bytes_sent} "%{NOTDQUOTE:http_referer}" "%{NOTDQUOTE:http_user_agent}"(?: %{NUMBER:request_length} %{NUMBER:request_time} \[%{DATA:proxy_upstream_name}\] \[%{DATA:proxy_alternative_upstream_name}\])?'29 apply_on: message30 statics:31 - meta: log_type32 value: http_access-log33 - target: evt.StrTime34 expression: evt.Parsed.time_local35 - grok:36 pattern: '%{NOTSPACE:target_fqdn}(?::%{INT:port})? %{IPORHOST:remote_addr} - %{NGCUSTOMUSER:remote_user} \[%{HTTPDATE:time_local}\] "%{DATA:request}" %{NUMBER:status} %{NUMBER:body_bytes_sent} "%{NOTDQUOTE:http_referer}" "%{NOTDQUOTE:http_user_agent}"(?: %{NUMBER:request_length} %{NUMBER:request_time} \[%{DATA:proxy_upstream_name}\] \[%{DATA:proxy_alternative_upstream_name}\])?'37 apply_on: message38 statics:39 - meta: log_type40 value: http_access-log41 - target: evt.StrTime42 expression: evt.Parsed.time_local43 - grok:44 pattern: '(%{IPORHOST:target_fqdn} )?%{NGINXERRTIME:time} \[%{LOGLEVEL:loglevel}\] %{NONNEGINT:pid}#%{NONNEGINT:tid}: (\*%{NONNEGINT:cid} )?%{GREEDYDATA:message}, client: %{IPORHOST:remote_addr}, server: %{DATA:target_fqdn}, request: "%{WORD:verb} ([^/]+)?%{NGCUSTOMURIPATHPARAM:request}( HTTP/%{NUMBER:http_version})?", host: "%{IPORHOST}(:%{NONNEGINT})?"'45 apply_on: message46 statics:47 - meta: log_type48 value: http_error-log49 - target: evt.StrTime50 expression: evt.Parsed.time51 pattern_syntax:52 NO_DOUBLE_QUOTE: '[^"]+'53 onsuccess: next_stage54 nodes:55 - filter: "evt.Parsed.message contains 'was not found in'"56 pattern_syntax:57 USER_NOT_FOUND: 'user "%{NO_DOUBLE_QUOTE:username}" was not found in "%{NO_DOUBLE_QUOTE}"'58 grok:59 pattern: "%{USER_NOT_FOUND}"60 apply_on: message61 statics:62 - meta: sub_type63 value: "auth_fail"64 - meta: username65 expression: evt.Parsed.username66 - filter: "evt.Parsed.message contains 'password mismatch'"67 pattern_syntax:68 PASSWORD_MISMATCH: 'user "%{NO_DOUBLE_QUOTE:username}": password mismatch'69 grok:70 pattern: "%{PASSWORD_MISMATCH}"71 apply_on: message72 statics:73 - meta: sub_type74 value: "auth_fail"75 - meta: username76 expression: evt.Parsed.username77 - filter: "evt.Parsed.message contains 'limiting requests, excess'"78 statics:79 - meta: sub_type80 value: "req_limit_exceeded"81statics:82 - meta: service83 value: http84 - target: evt.Parsed.src_ip85 expression: evt.Parsed.remote_addr86 - target: evt.Parsed.method87 expression: evt.Parsed.verb88 - target: evt.Parsed.path89 expression: evt.Parsed.request90 - target: evt.Parsed.size91 expression: evt.Parsed.body_bytes_sent92 - target: evt.Parsed.referer93 expression: evt.Parsed.http_referer94 - target: evt.Parsed.agent95 expression: evt.Parsed.http_user_agent96 - meta: source_ip97 expression: "evt.Parsed.remote_addr"98 - meta: http_status99 expression: "evt.Parsed.status"100 - meta: http_path101 expression: "evt.Parsed.request"102 - meta: http_verb103 expression: "evt.Parsed.verb"104 - meta: http_user_agent105 expression: "evt.Parsed.http_user_agent"106 - meta: target_fqdn107 expression: "evt.Parsed.target_fqdn"108 - meta: request_id109 expression: evt.Parsed.request_id110