Updated 2024-12-06: Updated both scripts, using newer suricata-update from get-go, updated classification.config, some minor adjusting of content.

Updated 2024-12-18: Corrected a typo in ‘suricatamod.sh‘, there was an extra space in two places

Updated 2024-12-19: As of OPNSense 24.7.11_2 we should have access to the latest ‘suricata-update‘ feature and you should not need to do the ‘git clone ...suricata-update step or the ln of python3 to python step, just delete the bolded path element in the ‘suricataupdate.sh‘ to use the already installed feature, in the future I will be re-writing parts of this to resolve this availability but let users using older versions of OPNSense know that they gotta update their OPNSense or git clone

Updated 2025-01-20: Added Github link with a Repo containing only the ‘suricata-update‘ support yaml files and a custom.yaml file – please use these and consider that each IDS installation is going to be unique to some degree, you will likely have to modify one or more elements of the ‘custom.yaml‘ to fit your network’s subnet, if not more

Following this guide will give you a very granular level of control over all Suricata SIDs/Rules, the ability to reduce false positives, and enable IPS Mode or Crowdsec Responder scenarios that work efficiently without causing outages.

You may have to check the Rules that are active and watch the log flow for events for optimal results, this guide does assume the user/admin has this part of the maintenance of an IDS/IPS covered.

The built-in Policy based rule management on the OPNSense is not only quite fast, it allows for some meta pattern based enable/disable of rules. Truly, it is a good system and quite efficient! While this speed and level of control is useful, if you want to modify rules and resolve FPs in sometimes very needful and useful ways you generally will be after suricata-update and its disable/enable/drop/modify pattern matched and/or specific SIDs after downloading updated rules.

Currently, and I want to see if I can help ‘enable’ this feature as a natural option (suricata-update in the latest release 7.0.8 should allow it to operate much faster), you must be able to do a few things.

Requirements to start:

  • Have a working OPNSense router
  • Already have Interfaces configured for Suricata/IDS to watch
  • Be able to log in to admin/root
  • Be able to reach the internet with the router

Overall steps:

  • Create a script for ensuring conf files are maintained (only needed for now, once this reaches a higher maturity we should resolve use of the ‘custom.yaml’ Suricata config file)
  • Create a script for running suricata-update with flags and restarting Suricata if necessary
  • Disable the default IDS Rule Update Web GUI Cron entry
  • Create a new configd file for new Web GUI Cron entries (Suricata State, Suricata Update)
  • Make a few Web GUI Cron settings changes
  • Maintain a small group of files (yaml/rules/conf) in a directory on your device to attain a complete OPNSense + Suricata-Update expression

If you haven’t heard about “suricata-update“, but you might be familiar with “oinkmaster” or “pulled-pork” of what IDS gurus used to use with Snort, and like those it updates the rules ecosystem, but of course works with Suricata. It is also already installed in your OPNSense Router/Firewall, so there’s really no new software/plug-in you have to install, you’ve already got the binaries/python bits. Until Suricata 7.0.8 is in OPNSense, the git repo clone for the ‘suricata-update’ latest release is a significant rule processing boost. If you don’t want to do this you can depending on the amount of rule sources you enable, and the disable/enable/drop/mod you configure, can expect ‘rule update’ times to be anywhere from 30 minutes to getting close to 2 hours. The line mentioning ‘suricata-update’ in the ‘suricataupdate.sh‘ file creation, if you were to remove the ‘/root/suricata-update/bin/‘ (bolded in example) from that line, it would use the version already installed.

This guide expects the user to be familiar with the OPNSense/FreeBSD to a point, and certainly able to get into the OPNSense Shell via SSH and to know how to create folders and files (you can get vim by pkg install vim). The scripts you will be creating do some logging to log files in the /root/ folder but attempts have been made to minimize the outputs.

This blog post will get updated and eventually will have a Github git repo with a default file set, possibly some updates to help setup and re-align the OPNSense – as of yet, I’m merely sharing how to get started.

That said, let’s go!

Disable Cron for OPNSense Policy based updates

Un-check the “enabled” box for the “IDS Rule Updates”, hit Save, then hit Apply

Create new actions configd Cron list file

This file will be located at:
/usr/local/opnsense/service/conf/actions.d/actions_homelab.conf

This does refer to two files we will be creating later. We need this file to list Cron entries on the OPNSense Web GUI.

actions_homelab.conf

[cfidsreload]
command: /root/suricatamod.sh; exit 0
parameters:
type:script
message:copy over and reload intrusion detection custom conf
description:Copy over and reload intrusion detection custom conf

[cfgidsupdate]
command: /root/suricataupdate.sh; exit 0
parameters:
type:script
message:download and update and mod suricata rules
description:Download and update and mod Suricata Rules

Update the configd service

To have the new Cron entries show up in the OPNSense Web GUI, you must restart the configd service, we will enable them later

service configd restart

Create custom file modification script

This file will be located at:
/root/suricatamod.sh

This does some work to keep the “custom.yaml” file and a few other file that enable the natural state of Suricata to work as expected. In short, the custom.yaml file gets over-wrote in some restart/rule update py script from OPNSense – so this would revert the file if it gets overwrote (such as it would by an update).

suricatamod.sh

#!/bin/sh
#set -x

# Get current date and time
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")

# Define file paths
# Suricata config custom
ROOT_CUSTOM1="/root/suricata/custom.yaml"
SURICATA_CUSTOM1="/usr/local/etc/suricata/custom.yaml"
# Installed Rules File
ROOT_CUSTOM2="/root/suricata/installed_rules.yaml"
SURICATA_CUSTOM2="/usr/local/etc/suricata/installed_rules.yaml"
# Installed Classification File
ROOT_CLASSIFICATION="/root/suricata/classification.config"
SURICATA_CLASSIFICATION1="/usr/local/etc/suricata/opnsense.rules/classification.config"
SURICATA_CLASSIFICATION2="/usr/local/etc/suricata/classification.config"
# Extra - cannot use this file currently
#SURICATA_TEMPLATE="/usr/local/opnsense/service/templates/OPNsense/IDS/custom.yaml"

# Start Logging
echo "$TIMESTAMP: File mod - Checking for configuration updates..." > /root/suricatamod.log

# Check for OPNSense running updates
script_name="rule-updater.py"
# Check if the script is running using ps
ps aux | grep "$script_name" | grep -v grep > /dev/null
if [ $? -eq 0 ]; then
  echo "$TIMESTAMP: File mod - Script '$script_name' is already running... exiting." >> /root/suricatamod.log
  exit 0 # Exit with a 0
else
  echo "$TIMESTAMP: File mod - Script '$script_name' is not running. Proceeding..." >> /root/suricatamod.log
fi
script_name="installRules.py"
# Check if the script is running using ps
ps aux | grep "$script_name" | grep -v grep > /dev/null
if [ $? -eq 0 ]; then
  echo "$TIMESTAMP: File mod - Script '$script_name' is already running... exiting." >> /root/suricatamod.log
  exit 0 # Exit with a 0
else
  echo "$TIMESTAMP: File mod - Script '$script_name' is not running. Proceeding..." >> /root/suricatamod.log
fi

# Update Public IP in custom.yaml
cp custom.yaml verycustom.yaml
public_ip=$(curl -s whoami.nova-labs.net | jq -r '.ip')
#sed -i .bak -e 's/EXTERNAL_IP=\"\d+\.\d+\.\d+\.\d+\"/EXTERNAL_IP="$public_ip"/' verycustom.yaml
sed -i '.bak' "s#EXTERNAL_IP: \"[0-9.].*\"#EXTERNAL_IP: \"${public_ip}\"#" custom.yaml

# Check our files and fix them if necessary
RELOAD_NEEDED="NO"
RESTART_NEEDED="NO"

# Check if files are identical
if cmp -s "$ROOT_CUSTOM1" "$SURICATA_CUSTOM1"; then
  echo "$TIMESTAMP: File mod - $ROOT_CUSTOM1 / $SURICATA_CUSTOM1 Files are identical." >> /root/suricatamod.log
else
  echo "$TIMESTAMP: File mod - Different, copying $ROOT_CUSTOM1 to $SURICATA_CUSTOM1" >> /root/suricatamod.log
  cp "$ROOT_CUSTOM1" "$SURICATA_CUSTOM1"
  RELOAD_NEEDED="YES"
fi

# Check if files are identical
if cmp -s "$ROOT_CUSTOM2" "$SURICATA_CUSTOM2"; then
  echo "$TIMESTAMP: File mod - $ROOT_CUSTOM2 / $SURICATA_CUSTOM2 Files are identical." >> /root/suricatamod.log
else
  echo "$TIMESTAMP: File mod - Different, copying $ROOT_CUSTOM2 to $SURICATA_CUSTOM2" >> /root/suricatamod.log
  cp "$ROOT_CUSTOM2" "$SURICATA_CUSTOM2"
  RELOAD_NEEDED="YES"
fi

# Check if files are identical
if cmp -s "$ROOT_CLASSIFICATION" "$SURICATA_CLASSIFICATION1"; then
  echo "$TIMESTAMP: File mod - $ROOT_CLASSIFICATION / $SURICATA_CLASSIFICATION1 Files are identical." >> /root/suricatamod.log
else
  echo "$TIMESTAMP: File mod - Different, copying $ROOT_CLASSIFICATION to $SURICATA_CLASSIFICATION1" >> /root/suricatamod.log
  cp "$ROOT_CLASSIFICATION" "$SURICATA_CLASSIFICATION1"
  RESTART_NEEDED="YES"
fi

# Check if files are identical
if cmp -s "$ROOT_CLASSIFICATION" "$SURICATA_CLASSIFICATION2"; then
  echo "$TIMESTAMP: File mod - $ROOT_CLASSIFICATION / $SURICATA_CLASSIFICATION2 Files are identical." >> /root/suricatamod.log
else
  echo "$TIMESTAMP: File mod - Different, copying $ROOT_CLASSIFICATION to $SURICATA_CLASSIFICATION2" >> /root/suricatamod.log
  cp "$ROOT_CLASSIFICATION" "$SURICATA_CLASSIFICATION2"
  RESTART_NEEDED="YES"
fi

# Check if rule update in process
if [ -e /root/suricata/inRunSuricataUpdate ]; then
  echo "$TIMESTAMP: File mod - Rule update in progress, not restarting... exiting." > /root/suricataxtramod.log
  exit 0 # Exit with a 0
fi

# We are not updating rules, so let us restart Suricata
if [ "$RESTART_NEEDED" == "YES" ]; then
  service suricata restart
  #pkill -USR2 suricata
  echo "$TIMESTAMP: File mod - Suricata service restarted." >> /root/suricatarestart.log
  exit 0
fi  

# We are not updating rules, so let us reload Suricata
if [ "$RELOAD_NEEDED" == "YES" ]; then
  #service suricata restart
  pkill -USR2 suricata
  echo "$TIMESTAMP: File mod - Suricata service reloaded." >> /root/suricatarestart.log
fi  
exit 0

Be sure to “chmod +x /root/suricatamod.sh

Get faster suricata-update Processing

Get a “python” executable as ‘suricata-update’ looks for Python not Python3:
ln -s /usr/local/bin/python3 /usr/local/bin/python

Then clone the Git repo:
git clone https://github.com/OISF/suricata-update.git

Create suricata-update runner (runs suricata-update and controls additional state)

This file will be located at:
/root/suricataupdate.sh

This runs the “suricata-update” python script with flags set for our OPNSense environment, it does a few checks to make sure it should run and if to restart Suricata or not.

suricataupdate.sh

#!/bin/sh

# Get current date and time
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")

# Check if I am already running
if [ ! -e /root/suricata/inRunSuricataUpdate ]; then
  touch /root/suricata/inRunSuricataUpdate
else
  echo "$TIMESTAMP: Rule update - I am already running... exiting." > /root/suricataxtrarules.log
  exit 0 # Exit with a 0
fi

cd /root/suricata ; git pull ; cd /root/

# Define file paths
ROOT_CUSTOM1="/root/suricata/suricata.rules"
SURICATA_CUSTOM1="/usr/local/etc/suricata/opnsense.rules/suricata.rules"
echo "$TIMESTAMP: Rule update - Checking for rule updates..." > /root/suricatarules.log

script_name="rule-updater.py"
# Check if the script is running using ps
ps aux | grep "$script_name" | grep -v grep > /dev/null
if [ $? -eq 0 ]; then
  echo "$TIMESTAMP: Rule update - Script '$script_name' is already running... exiting." >> /root/suricatarules.log
  exit 0 # Exit with a 0
else
  echo "$TIMESTAMP: Rule update - Script '$script_name' is not running. Proceeding..." >> /root/suricatarules.log
fi

script_name="installRules.py"
# Check if the script is running using ps
ps aux | grep "$script_name" | grep -v grep > /dev/null
if [ $? -eq 0 ]; then
  echo "$TIMESTAMP: Rule update - Script '$script_name' is already running... exiting." >> /root/suricatarules.log
  exit 0 # Exit with a 0
else
  echo "$TIMESTAMP: Rule update - Script '$script_name' is not running. Proceeding..." >> /root/suricatarules.log
fi

# Backup existing rules
cp "$SURICATA_CUSTOM1" "$ROOT_CUSTOM1"

# Run suricata-update
/root/suricata-update/bin/suricata-update update --config /root/suricata/update.yaml --suricata-conf /usr/local/etc/suricata/suricata.yaml --suricata /usr/local/bin/suricata --data-dir /usr/local/etc/suricata --threshold-in=/root/suricata/threshold.in --threshold-out=/usr/local/etc/suricata/threshold.config --output /usr/local/etc/suricata/opnsense.rules -v --no-test --no-reload 2>&1 | tee /root/suricataupdate.log

cp /usr/local/etc/suricata/opnsense.rules/classification.config /root/suricata/classification.config.bak
cp /root/suricata/classification.config /usr/local/etc/suricata/opnsense.rules/classification.config
cp /root/suricata/classification.config /usr/local/etc/suricata/classification.config

# Make some check files to track the classtype to classification definitions
grep -Eo 'classtype:([^\;]+)' /usr/local/etc/suricata/opnsense.rules/suricata.rules | sed 's/classtype: /classtype:/g' | sort | uniq | cut -d':' -f2 > /root/suricata/classtype.list 
grep -Eo 'classification: ([^\,]+)' /root/suricata/classification.config | cut -d' ' -f2 | sort > /root/suricata/classification.list
echo "$TIMESTAMP: Rule update - ==New classtype needing classification==" >> /root/suricatarules.log
diff /root/suricata/classtype.list /root/suricata/classification.list | grep '<' >> /root/suricatarules.log
echo "$TIMESTAMP: Rule update - ========================================" >> /root/suricatarules.log

# Check for Suricata restart / reload condition
RESTART_NEEDED="NO"

# Check if files are identical
if cmp -s "$ROOT_CUSTOM1" "$SURICATA_CUSTOM1"; then
  echo "$TIMESTAMP: Rule update - $ROOT_CUSTOM1 Files are identical." >> /root/suricatarules.log
else
  echo "$TIMESTAMP: Rule update - Rules file is different, restarting Suricata" >> /root/suricatarules.log
  RESTART_NEEDED="YES"
fi

if [ "$RESTART_NEEDED" == "YES" ]; then
  #service suricata restart
  pkill -USR2 suricata
  echo "$TIMESTAMP: Rule update - Suricata service restarted." >> /root/suricatarestart.log
fi  

# Removing run check
rm /root/suricata/inRunSuricataUpdate

exit 0

Be sure to “chmod +x /root/suricataupdate.sh

Create a Folder for Suricata-Update Yaml/Conf Files

Let’s create the folder and run a command to create our default Suricata-Update conf files.

mkdir /root/suricata
cd /root/suricata

Create suricata-update update.yaml file

This file will be located at:
/root/suricata/update.yaml

This configures suricata-update, and we specify our ‘snortrules’ grab here because I haven’t figured out how to get the suricata-upate for the “oinkcode” to work and this works.

update.yaml

# Configuration with disable filters.
disable-conf: /root/suricata/disable.conf

# Configuration with enable filters.
enable-conf: /root/suricata/enable.conf

# Configuration with drop filters.
drop-conf: /root/suricata/drop.conf

# Configuration with modify filters.
modify-conf: /root/suricata/modify.conf

# List of files to ignore. Overrided by the --ignore command line option.
ignore:
  - "*deleted.rules"

# Override the user-agent string.
#user-agent: "Suricata-Update"

# Provide an alternate command to the default test command.
#test-command: ${SURICATA_PATH} -T -S ${OUTPUT_FILENAME} -l /tmp

# Provide a command to reload the Suricata rules.
#reload-command: sudo systemctl reload suricata

# Remote rule sources. Simply a list of URLs.
sources:
  - "https://www.snort.org/rules/snortrules-snapshot-29111.tar.gz?oinkcode=aaaaaaaaaaaaaaaaaaredactedaaaaaaaaaaaaaa"

# A list of local rule sources. Each entry can be a rule file, a
# directory or a wild card specification.
local:
  - "/root/suricata/homelab.rules"

Create your homelab.rules file to bypass / home rule

This will be located at:
/root/suricata/homelab.rules

This is a great place to get your IDS to focus on the important by NOT focusing on the unimportant, a pass+bypass rule on host paths that you do not want inspected will lower CPU utilization and cost!

homelab.rules

pass ip $GAMING_HOSTS any -> any any (msg:"Gaming Hosts"; bypass; sid:1000001; rev:1;)
pass ip any any -> $GAMING_HOSTS any (msg:"Gaming Hosts"; bypass; sid:1100001; rev:1;)

Create your custom.yaml file to customize suricata further

This file will be located at:
/root/suricata/custom.yaml

We have talked about this file before on this site, here we mention it because this is how you will get your “address-groups” to us in your rules. In our example scripts here, we have setup a “Gaming Hosts” group to be bypassed in our “homelab.rules” and/or your could use “modify.conf” and change the text of “$HOME_NET” in rules to “[$HOME_NET, !$GAMING_HOSTS]” and thus ignore those hosts. No more False Positives on from those hosts!

custom.yaml

%YAML 1.1
---
vars:                                                                           
  address-groups:                                                               
    HOME_NET: "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]"                       
    EXTERNAL_NET: "!$HOME_NET"                                                  
    HTTP_SERVERS: "$HOME_NET"                                                   
    SMTP_SERVERS: "$HOME_NET"                                                   
    SQL_SERVERS: "$HOME_NET"                                                    
    DNS_SERVERS: "$HOME_NET"                                                    
    TELNET_SERVERS: "$HOME_NET"                                                 
    AIM_SERVERS: "$EXTERNAL_NET"                                                
    DC_SERVERS: "$HOME_NET"                                                     
    DNP3_SERVER: "$HOME_NET"                                                    
    DNP3_CLIENT: "$HOME_NET"                                                    
    MODBUS_CLIENT: "$HOME_NET"                                                  
    MODBUS_SERVER: "$HOME_NET"                                                  
    ENIP_CLIENT: "$HOME_NET"                                                    
    ENIP_SERVER: "$HOME_NET"                                                    
    SIP_SERVERS: "$HOME_NET"
    EXTERNAL_IP: "0.0.0.0"
    XBOX_HOSTS: "[10.30.15.16,10.30.15.17]"
    SWITCH_HOSTS: "[]"
    PS_HOSTS: "[]"
    GAMING_HOSTS: "[$XBOX_HOSTS,$SWITCH_HOSTS,$PS_HOSTS]"
  port-groups:                                                                  
    HTTP_PORTS: "[80]"
    SHELLCODE_PORTS: "!80"                                                      
    ORACLE_PORTS: 1521                                                          
    SSH_PORTS: 22                                                               
    DNP3_PORTS: 20000                                                           
    MODBUS_PORTS: 502                                                           
    FILE_DATA_PORTS: "[$HTTP_PORTS,110,143]"                                    
    FTP_PORTS: 21                                                               
    GENEVE_PORTS: 6081                                                          
    VXLAN_PORTS: 4789
    TEREDO_PORTS: 3544
    SIP_PORTS: "[5060,5061]"
outputs:
  - eve-log:
      enabled: yes
      filetype: regular
      filename: evexff.json
      metadata: yes
      pcap-file: false
      community-id: true
      community-id-seed: 0
      types:
        - alert:
            payload: no
            payload-printable: no
            packet: no
            http-body: no
            http-body-printable: no
            tagged-packets: yes
            metadata:
              app-layer: true
              flow: true
              rule:
                metadata: true
                raw: true
            xff:
              enabled: yes
              mode: overwrite
              deployment: reverse
              header: X-Forwarded-For
        - frame:
            enabled: no
        - anomaly:
            enabled: no
            types:
              applayer: no
  - eve-log:
      enabled: yes
      filetype: regular
      filename: eve.json
      metadata: yes
      pcap-file: false
      community-id: true
      community-id-seed: 0
      types:
        - alert:
            payload: yes
            payload-buffer-size: 100kb
            payload-printable: yes
            packet: yes
            http-body: yes
            http-body-printable: yes
            tagged-packets: yes
            metadata:
              app-layer: true
              flow: true
              rule:
                metadata: true
                raw: true
            xff:
              enabled: yes
              mode: extra-data
              deployment: reverse
              header: X-Forwarded-For
        - frame:
            enabled: no
        - anomaly:
            enabled: no
  - eve-log:
      enabled: yes
      filetype: syslog
      identity: "suricata"
      facility: local5
      level: Info
      metadata: yes
      pcap-file: false
      community-id: true
      community-id-seed: 0
      types:
        - alert:
            payload: yes
            payload-buffer-size: 100kb
            payload-printable: yes
            packet: no
            http-body: no
            http-body-printable: no
            tagged-packets: yes
            metadata:
              app-layer: true
              flow: true
              rule:
                metadata: true
                raw: true
            xff:
              enabled: yes
              mode: extra-data
              deployment: reverse
              header: X-Forwarded-For
        - frame:
            enabled: no
        - anomaly:
            enabled: no
            types:
              applayer: no
  - unified2-alert:
      enabled: no
  - http-log:
      enabled: no
      filename: http.log
      append: yes
  - tls-log:
      enabled: no
      filename: tls.log
      append: yes
  - tls-store:
      enabled: no
  - pcap-log:
      enabled: no
      filename: log.pcap
      limit: 1000mb
      max-files: 2000
      compression: none
      mode: normal
      use-stream-depth: no
      honor-pass-rules: no
  - alert-debug:
      enabled: no
      filename: alert-debug.log
      append: yes
  - alert-prelude:
      enabled: no
      profile: suricata
      log-packet-content: no
      log-packet-header: yes
  - stats:
      enabled: yes
      filename: stats.log
      append: yes
      totals: yes
      threads: no
  - file-store:
      version: 2
      enabled: no
      xff:
        enabled: no
        mode: extra-data
        deployment: reverse
        header: X-Forwarded-For
  - file-store:
      enabled: no
  - tcp-data:
      enabled: no
      type: file
      filename: tcp-data.log
  - http-body-data:
      enabled: no
      type: file
      filename: http-data.log
  - lua:
      enabled: no
      scripts:
app-layer:
  protocols:
    telnet:
      enabled: yes
    rfb:
      enabled: yes
      detection-ports:
        dp: 5900, 5901, 5902, 5903, 5904, 5905, 5906, 5907, 5908, 5909
    mqtt:
      enabled: yes
    krb5:
      enabled: yes
    bittorrent-dht:
      enabled: yes
    snmp:
      enabled: yes
    ike:
      enabled: yes
    tls:
      enabled: yes
      detection-ports:
        dp: 443
      ja3-fingerprints: auto
    pgsql:
      enabled: yes
      stream-depth: 0
      max-tx: 1024
    dcerpc:
      enabled: yes
    ftp:
      enabled: yes
    rdp:
    ssh:
      enabled: yes
    http2:
      enabled: yes
    smtp:
      enabled: yes
      raw-extraction: no
      mime:
        decode-mime: yes
        decode-base64: yes
        decode-quoted-printable: yes
        header-value-depth: 2000
        extract-urls: yes
        body-md5: no
      inspected-tracker:
        content-limit: 100000
        content-inspect-min-size: 32768
        content-inspect-window: 4096
    imap:
      enabled: detection-only
    smb:
      enabled: yes
      detection-ports:
        dp: 139, 445
    nfs:
      enabled: yes
    tftp:
      enabled: yes
    dns:
      tcp:
        enabled: yes
        detection-ports:
          dp: 53
      udp:
        enabled: yes
        detection-ports:
          dp: 53
    http:
      enabled: yes
      libhtp:
         default-config:
           personality: IDS
           request-body-limit: 100kb
           response-body-limit: 100kb
           request-body-minimal-inspect-size: 32kb
           request-body-inspect-window: 4kb
           response-body-minimal-inspect-size: 40kb
           response-body-inspect-window: 16kb
           response-body-decompress-layer-limit: 2
           http-body-inline: auto
           swf-decompression:
             enabled: no
             type: both
             compress-depth: 100kb
             decompress-depth: 100kb
           double-decode-path: no
           double-decode-query: no
         server-config:
    modbus:
      enabled: yes
      detection-ports:
        dp: 502
      stream-depth: 0
    dnp3:
      enabled: yes
      detection-ports:
        dp: 20000
    enip:
      enabled: yes
      detection-ports:
        dp: 44818
        sp: 44818
    ntp:
      enabled: yes
    quic:
      enabled: yes
    dhcp:
      enabled: yes
    sip:
      enabled: yes
asn1-max-frames: 256
datasets:
  defaults:
  rules:
host-os-policy:
  windows: [10.30.1.0/24]
  bsd: []
  bsd-right: []
  old-linux: []
  linux: [10.33.14.0/24]
  old-solaris: []
  solaris: []
  hpux10: []
  hpux11: []
  irix: []
  macos: []
  vista: []
  windows2k3: []                                                                                                      
classification-file: /usr/local/etc/suricata/classification.config
reference-config-file: /usr/local/etc/suricata/reference.config
threshold-file: /usr/local/etc/suricata/threshold.config

Create a new “installed_rules.yaml” and

This file will be located at:
/root/suricata/installed_rules.yaml

This insures that our “suricata.rules” file is used as expected and our “homelab.rules” is referenced, this step might not be as necessary as it was initially for me, but I leave it here to ensure success.

installed_rules.yaml

%YAML 1.1
---
rule-files:
  - suricata.rules
  - builtin.rules
  - homelab.rules

Copy “installed_rules.yaml” to Suricata’s directory

cp /root/suricata/installed_rules.yaml /usr/local/etc/suricata/installed_rules.yaml

Create disable/enable/drop/modify default conf files

cd /root/suricata
suricata-update --dump-sample-configs

Enable Suricata Update Sources

Update your Suricata sources:

suricata-update update-sources --config /root/suricata/update.yaml --suricata-conf /usr/local/etc/suricata/suricata.yaml --suricata /usr/local/bin/suricata --data-dir /usr/local/etc/suricata

List the available rule update sources:

suricata-update list-sources --config /root/suricata/update.yaml --suricata-conf /usr/local/etc/suricata/suricata.yaml --suricata /usr/local/bin/suricata --data-dir /usr/local/etc/suricata

Enable a source, in this example “et/open”:

suricata-update enable-source et/open --config /root/suricata/update.yaml --suricata-conf /usr/local/etc/suricata/suricata.yaml --suricata /usr/local/bin/suricata --data-dir /usr/local/etc/suricata

List all enabled sources:

suricata-update list-sources --enabled --config /root/suricata/update.yaml --suricata-conf /usr/local/etc/suricata/suricata.yaml --suricata /usr/local/bin/suricata --data-dir /usr/local/etc/suricata

Create new Web GUI Cron Entries

Go to System > Settings > Cron and create the following entries:

In Edit job, check-mark “enabled”, set minutes to a high frequency (*/5 or */10), the rest of time fields at “*“, and select “Copy over and reload intrusion detection custom…” as the Command, enter your own Description, hit “Save”

In Edit job, check-mark “enabled”, set to multiple times a day (*/6 for 4 times a day) putting a minute in and “*” for the rest of the time fields, and set Command to “Download and update and mod Suricata Rules”, enter your own Description, hit “Save”, then hit “Apply”

Create the classification.conf file

If you are using CrowdSec and watching your Suricata logs/evelogs, I suggest this file to update the priority/classification of your rules, this is a bit more open but can be locked down by setting 2->1, and 3->2 and so on.

This file will be located at:
/root/suricata/classification.conf

classification.conf

config classification: not-suspicious,Not Suspicious Traffic,3
config classification: unknown,Unknown Traffic,3
config classification: bad-unknown,Potentially Bad Traffic, 2
config classification: attempted-recon,Attempted Information Leak,1
config classification: successful-recon-limited,Information Leak,1
config classification: successful-recon-largescale,Large Scale Information Leak,2
config classification: attempted-dos,Attempted Denial of Service,1
config classification: successful-dos,Denial of Service,1
config classification: attempted-user,Attempted User Privilege Gain,1
config classification: unsuccessful-user,Unsuccessful User Privilege Gain,1
config classification: successful-user,Successful User Privilege Gain,1
config classification: attempted-admin,Attempted Administrator Privilege Gain,1
config classification: successful-admin,Successful Administrator Privilege Gain,1
config classification: rpc-portmap-decode,Decode of an RPC Query,2
config classification: shellcode-detect,Executable code was detected,3
config classification: string-detect,A suspicious string was detected,3
config classification: suspicious-filename-detect,A suspicious filename was detected,2
config classification: suspicious-login,An attempted login using a suspicious username was detected,2
config classification: system-call-detect,A system call was detected,2
config classification: tcp-connection,A TCP connection was detected,4
config classification: trojan-activity,A Network Trojan was detected,1
config classification: unusual-client-port-connection,A client was using an unusual port,3
config classification: network-scan,Detection of a Network Scan,2
config classification: denial-of-service,Detection of a Denial of Service Attack,2
config classification: non-standard-protocol,Detection of a non-standard protocol or event,3
config classification: protocol-command-decode,Generic Protocol Command Decode,3
config classification: web-application-activity,access to a potentially vulnerable web application,2
config classification: web-application-attack,Web Application Attack,1
config classification: misc-activity,Misc activity,3
config classification: misc-attack,Misc Attack,3
config classification: icmp-event,Generic ICMP event,3
config classification: inappropriate-content,Inappropriate Content was Detected,3
config classification: policy-violation,Potential Corporate Privacy Violation,3
config classification: default-login-attempt,Attempt to login by a default username and password,2
config classification: targeted-activity,Targeted Malicious Activity was Detected,1
config classification: exploit-kit,Exploit Kit Activity Detected,1
config classification: external-ip-check,Device Retrieving External IP Address Detected,3
config classification: domain-c2,Domain Observed Used for C2 Detected,1
config classification: pup-activity,Possibly Unwanted Program Detected,2
config classification: credential-theft,Successful Credential Theft Detected,1
config classification: social-engineering,Possible Social Engineering Attempted,3
config classification: coin-mining,Crypto Currency Mining Activity Detected,3
config classification: command-and-control,Malware Command and Control Activity Detected,1
config classification: sdf,Sensitive Data File Content Detected,3
config classification: file-transfer,File-Transfer app detection by OPNsense,3
config classification: mail,Mailprovider app detection by OPNsense,3
config classification: media-streaming,Media-Streaming app detection by OPNsense,3
config classification: messaging,Messenger app detection by OPNsense,3
config classification: social-media,Social-Media app detection by OPNsense,3
config classification: test,OPNsense Test Rules,3
config classification: uncategorized,Uncategorized app detection by OPNsense,3
config classification: file-format,Known malicious file or file based exploit,1
config classification: malware-cnc,Known malware command and control traffic,1
config classification: client-side-exploit,Known client side exploit attempt,1

That’s all folks!

Running ./suricatamod.sh and then running ./suricataupdate.sh should cause files to be in the right places, and rule update to complete with appropriate reload/restart for Suricata. You will have to enable sources via suricata-update commands listed above, update the update.yaml and then the disable/enable/drop/modify configs to your liking (as well as your local rule file, I would suggest a labs/pass and a ids/alert style rule files plus updating the classification which affects the priority and there-by Crowdsec response level if you are using that).

I am still learning more about the classification.config file – and will update this post as I realize potentials.
https://forum.suricata.io/t/classification-config-which-source-and-how-to-control-output/5109

I have shared some suggested disable/enable/drop/modify configs. No expectation to be checking out the drop element until a later day – focusing on disable, enable, and modify for now to use with suricata-update to minimize FPs and other noisy SIDs.
https://github.com/j0nny55555/noiseless-suricata-update
Please feel free to clone/branch and suggest updates!

Best of luck, please comment and suggest updates/fixes if necessary!

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.