├── opnsense_naxsi_waf_event.yaml ├── opnsense_naxsi_logs.yaml └── README.md /opnsense_naxsi_waf_event.yaml: -------------------------------------------------------------------------------- 1 | type: trigger 2 | name: ls111/opnsense_naxsi_waf_event 3 | description: "Detects if NAXSI has triggered a security event in accordance with its WAF policies" 4 | filter: evt.Meta.log_type == 'waf_attack_event' 5 | groupby: evt.Meta.source_ip 6 | labels: 7 | service: naxsi 8 | type: waf_security_event 9 | remediation: true 10 | scope: 11 | type: Ip 12 | expression: evt.Meta.source_ip 13 | -------------------------------------------------------------------------------- /opnsense_naxsi_logs.yaml: -------------------------------------------------------------------------------- 1 | name: ls111/opnsense_naxsi_logs 2 | description: "Parse NAXSI error logs on OPNSense firewall" 3 | filter: "evt.Parsed.program == 'naxsi'" 4 | onsuccess: next_stage 5 | nodes: 6 | - grok: 7 | pattern: '%{DATESTAMP:timestamp} \[error] %{NUMBER}\#%{NUMBER}: \*%{NUMBER} NAXSI_FMT: ip=%{IPORHOST}&server=%{IPORHOST:serverip}&uri=%{URIPATHPARAM:server_uri}&vers=%{NUMBER}&total_processed=%{NUMBER}&total_blocked=%{NUMBER}&config=block&cscore0=%{DATA}&score0=%{NUMBER:score}&zone0=%{DATA}&id0=%{NUMBER:rule_id}&var_name0=%{DATA}&zone1=%{DATA}&id1=%{NUMBER}&var_name1=%{DATA}, client: %{IPORHOST:source_ip}, server: %{IPORHOST:server_hostname}, request: "%{DATA:request}", host: %{DATA}, referrer: "%{DATA:referrer}"' 8 | apply_on: message 9 | statics: 10 | - meta: log_type 11 | value: waf_attack_event 12 | - target: evt.StrTime 13 | expression: evt.Parsed.timestamp 14 | - meta: source_ip 15 | expression: evt.Parsed.source_ip 16 | - meta: http_request 17 | expression: evt.Parsed.request 18 | - meta: service 19 | value: naxsi 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OPNSense + CrowdSec + NAXSI WAF Integration 2 | Parsers and scenarios to allow CrowdSec to ban threat actors caught by NAXSI WAF on a OPNSense firewall deployment. 3 | ________ 4 | 5 | **Step 1:** Setup NAXSI WAF on your OPNSense firewall, you can follow this video for guidance:
6 | [OPNSense - Web Application Firewall (WAF) configuration using NAXSI](https://www.youtube.com/watch?v=IYDoQmUVdvU) 7 | 8 | **Step 2:** Install the CrowdSec plugin on your OPNSense firewall (I will be releasing a video about this and subsequent steps soon) 9 | 10 | **Step 3:** SSH into your OPNSense firewall head over to the following directory:
11 | 12 | ```cd /usr/local/etc/crowdsec``` 13 | 14 | **Step 4:** Edit and prepend the following to the top of your acquis.yaml (log acquisition) file: 15 | 16 | ``` 17 | filenames: 18 | #note: this below will need to point to your web app error log file. 19 | - /var/log/nginx/www.dvwa.local.error.log 20 | labels: 21 | type: naxsi 22 | --- 23 | ``` 24 | **Step 5:** Go to ```cd /usr/local/etc/crowdsec/parsers/s01-parse``` this is where we are going to create the custom parser .yaml file that parses through the NAXSI log file we included above, and matches the Grok pattern in the log. This will save you the efforts of doing this somewhat tedious task yourself. 25 | 26 | ``` 27 | name: ls111/opnsense_naxsi_logs 28 | description: "Parse NAXSI error logs on OPNSense firewall" 29 | filter: "evt.Parsed.program == 'naxsi'" 30 | onsuccess: next_stage 31 | nodes: 32 | - grok: 33 | pattern: '%{DATESTAMP:timestamp} \[error] %{NUMBER}\#%{NUMBER}: \*%{NUMBER} NAXSI_FMT: ip=%{IPORHOST}&server=%{IPORHOST:serverip}&uri=%{URIPATHPARAM:server_uri}&vers=%{NUMBER}&total_processed=%{NUMBER}&total_blocked=%{NUMBER}&config=block&cscore0=%{DATA}&score0=%{NUMBER:score}&zone0=%{DATA}&id0=%{NUMBER:rule_id}&var_name0=%{DATA}&zone1=%{DATA}&id1=%{NUMBER}&var_name1=%{DATA}, client: %{IPORHOST:source_ip}, server: %{IPORHOST:server_hostname}, request: "%{DATA:request}", host: %{DATA}, referrer: "%{DATA:referrer}"' 34 | apply_on: message 35 | statics: 36 | - meta: log_type 37 | value: waf_attack_event 38 | - target: evt.StrTime 39 | expression: evt.Parsed.timestamp 40 | - meta: source_ip 41 | expression: evt.Parsed.source_ip 42 | - meta: http_request 43 | expression: evt.Parsed.request 44 | - meta: service 45 | value: naxsi 46 | ``` 47 | 48 | **Step 6:** We now have to create the scenario .yaml file in ```cd /usr/local/etc/crowdsec/scenarios``` which takes the parsed log info and determines how CrowdSec should deal with it, in this case we are going to remediate the event by passing the source_ip (attacker ip) over to the firewall bouncer, which will then action the ban. 49 | ``` 50 | type: trigger 51 | name: ls111/opnsense_naxsi_waf_event 52 | description: "Detects if NAXSI has triggered a security event in accordance with its WAF policies" 53 | filter: evt.Meta.log_type == 'waf_attack_event' 54 | groupby: evt.Meta.source_ip 55 | labels: 56 | service: naxsi 57 | type: waf_security_event 58 | remediation: true 59 | scope: 60 | type: Ip 61 | expression: evt.Meta.source_ip 62 | ``` 63 | 64 | **Step 7:** We are now going to edit our profile.yaml file ```/usr/local/etc/crowdsec/profile.yaml``` and change the ban duration to 5 min for lab purposes so that you dont accidently lock yourself out for extended periods, the default for a production environment is 4 hours, but this can be anything you like. The profile controls how we should remediate an attack event and passes the source_ip over to the bouncer which creates the deny rules in PF which is the default firewall OPNSense/BSD uses 65 | 66 | ``` 67 | name: default_ip_remediation 68 | #debug: true 69 | filters: 70 | - Alert.Remediation == true && Alert.GetScope() == "Ip" 71 | decisions: 72 | - type: ban 73 | duration: 5m #duration of the ban set to 5 min, default is 4 hours, changed this for lab purposes. 74 | #duration_expr: Sprintf('%dh', (GetDecisionsCount(Alert.GetValue()) + 1) * 4) 75 | # notifications: 76 | # - slack_default # Set the webhook in /usr/local/etc/crowdsec/notifications> 77 | # - splunk_default # Set the splunk url and token in /usr/local/etc/crowdsec/> 78 | # - http_default # Set the required http parameters in /usr/local/etc/crowd> 79 | # - email_default # Set the required email parameters in /usr/local/etc/crow> 80 | on_success: break 81 | ``` 82 | 83 | **Step 8:** You need to restart the CrowdSec service: ```service crowdsec restart``` so that all the changes can take effect. 84 | 85 | If you attempt to simulate an injection attack against your web app, you will note that NAXSI intercepts this as well as bans the attacker IP address for the duration you specified making the WAF solution more complete. 86 | 87 | While OPNSense allows you to install the CrowdSec plugin using the GUI, you can only make partial changes to it using the GUI hence why we need to do everything in shell. CrowdSec comes with its own built in command line tool, access it by typing ```cscli -h``` and you will have full control over your CrowdSec deployment. 88 | 89 | ``` 90 | cscli is the main command to interact with your crowdsec service, scenarios & db. 91 | It is meant to allow you to manage bans, parsers/scenarios/etc, api and generally manage you crowdsec setup. 92 | 93 | Usage: 94 | cscli [command] 95 | 96 | Available Commands: 97 | alerts Manage alerts 98 | bouncers Manage bouncers [requires local API] 99 | capi Manage interaction with Central API (CAPI) 100 | collections Manage collections from hub 101 | completion Generate completion script 102 | config Allows to view current config 103 | console Manage interaction with Crowdsec console (https://app.crowdsec.net) 104 | dashboard Manage your metabase dashboard container [requires local API] 105 | decisions Manage decisions 106 | explain Explain log pipeline 107 | help Help about any command 108 | hub Manage Hub 109 | hubtest Run functional tests on hub configurations 110 | lapi Manage interaction with Local API (LAPI) 111 | machines Manage local API machines [requires local API] 112 | metrics Display crowdsec prometheus metrics. 113 | notifications Helper for notification plugin configuration 114 | parsers Install/Remove/Upgrade/Inspect parser(s) from hub 115 | postoverflows Install/Remove/Upgrade/Inspect postoverflow(s) from hub 116 | scenarios Install/Remove/Upgrade/Inspect scenario(s) from hub 117 | simulation Manage simulation status of scenarios 118 | version Display version and exit. 119 | 120 | Flags: 121 | -c, --config string path to crowdsec config file (default "/usr/local/etc/crowdsec/config.yaml") 122 | -o, --output string Output format : human, json, raw. 123 | --debug Set logging to debug. 124 | --info Set logging to info. 125 | --warning Set logging to warning. 126 | --error Set logging to error. 127 | --trace Set logging to trace. 128 | -h, --help help for cscli 129 | ``` 130 | --------------------------------------------------------------------------------