PKG_NAME:=snort3
PKG_VERSION:=3.1.76.0
-PKG_RELEASE:=1
+PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/snort3/snort3/archive/refs/tags/
$(INSTALL_CONF) \
./files/snort.config \
$(1)/etc/config/snort
- $(INSTALL_CONF) \
- ./files/local.lua \
- $(1)/etc/snort
- $(INSTALL_CONF) \
- ./files/homenet.lua \
- $(1)/etc/snort
+
sed \
- -i -e "/^EXTERNAL_NET\\s\\+=/ a include 'homenet.lua'" \
- -e "/^HOME_NET\\s\\+=/ i -- we set HOME_NET and EXTERNAL_NET here or via an included file" \
+ -i \
+ -e "/^-- HOME_NET and EXTERNAL_NET/ i -- The values for the two variables HOME_NET and EXTERNAL_NET have been" \
+ -e "/^-- HOME_NET and EXTERNAL_NET/ i -- moved to /etc/config/snort, so do not modify them here without good" \
+ -e "/^-- HOME_NET and EXTERNAL_NET/ i -- reason.\n" \
-e 's/^\(HOME_NET\s\+=\)/--\1/g' \
-e 's/^\(EXTERNAL_NET\s\+=\)/--\1/g' \
$(1)/etc/snort/snort.lua
+++ /dev/null
--- Unused when using 'snort-mgr', do not modify without deep understanding.
--- setup HOME_NET below with your IP range/ranges to protect
---HOME_NET = [[ 192.168.1.0/24 10.1.0.0/24 ]]
---EXTERNAL_NET = "!$HOME_NET"
+++ /dev/null
--- This file is no longer used if you are using 'snort-mgr' to create the
--- configuration. It is left as a sample.
---
--- use ths file to customize any functions defined in /etc/snort/snort.lua
-
--- switch tap to inline in ips and uncomment the below to run snort in inline mode
---snort = {}
---snort["-Q"] = true
-
-ips = {
- mode = tap,
- -- mode = inline,
- variables = default_variables,
- -- uncomment and change the below to reflect rules or symlinks to rules on your filesystem
- -- include = RULE_PATH .. '/snort.rules',
-}
-
-daq = {
- module_dirs = {
- '/usr/lib/daq',
- },
- modules = {
- {
- name = 'afpacket',
- mode = 'inline',
- }
- }
-}
-
-alert_syslog = {
- level = 'info',
-}
-
--- To log to a file, uncomment the below and manually create the dir defined in output.logdir
---output.logdir = '/var/log/snort'
---alert_fast = {
--- file = true,
--- packet = false,
---}
-
-normalizer = {
- tcp = {
- ips = true,
- }
-}
-
-file_policy = {
- enable_type = true,
- enable_signature = true,
- rules = {
- use = {
- verdict = 'log', enable_file_type = true, enable_file_signature = true
- }
- }
-}
-
--- To use openappid with snort, install the openappid package and uncomment the below
---appid = {
--- app_detector_dir = '/usr/lib/openappid',
--- log_stats = true,
--- app_stats_period = 60,
---}
action: config_item("enum", [ "alert", "block", "drop", "reject" ]),
interface: config_item("str", [ uci.get("network", "wan", "device") ]),
snaplen: config_item("range", [ 1518, 65535 ]), // int daq.snaplen = 1518: set snap length (same as -s) { 0:65535 }
+
+ include: config_item("path", [ "" ]), // User-defined snort configuration, applied at end of snort.lua.
};
const nfq_config = {
your lan range, default is '192.168.1.0/24'
external_net - IP range external to home. Usually 'any', but if you only
care about true external hosts (trusting all lan devices),
- then '!$HOMENET' or some specific range
+ then '!$HOME_NET' or some specific range
mode - 'ids' or 'ips', for detection-only or prevention, respectively
oinkcode - https://www.snort.org/oinkcodes
config_dir - Location of the base snort configuration files. Default /etc/snort
action - 'alert', 'block', 'reject' or 'drop'
method - 'pcap', 'afpacket' or 'nfq'
snaplen - int daq.snaplen = 1518: set snap length (same as -s) { 0:65535 }
+ include - User-defined snort configuration, applied at end of generated snort.lua
nfq - https://github.com/snort3/libdaq/blob/master/modules/nfq/README.nfq.md
queue_maxlen - nfq's '--daq-var queue_maxlen=int'
load_all();
-switch (getenv("TYPE")) {
+let table_type = getenv("TYPE");
+switch (table_type) {
case "snort":
render_snort();
return;
return;
default:
- print("Invalid table type.\n");
+ print(`Invalid table type '${table_type}', should be one of snort, nftables, config, help.\n`);
return;
}
chain {{ chain_type }}_{{ snort.mode }} {
type filter hook {{ chain_type }} priority {{ nfq.chain_priority }}
policy accept
- {% if (nfq.include) { include(nfq.include, { snort, nfq }); } %}
- # tcp flags ack ct direction original ct state established counter accept
+ {% if (nfq.include) {
+ // We use the ucode include here, so that the included file is also
+ // part of the template and can use values passed in from the config.
+ printf("\n\t\t#-- The following content included from '%s'\n", nfq.include);
+ include(nfq.include, { snort, nfq });
+ printf("\t\t#-- End of included file.\n\n");
+ } %}
counter queue flags bypass to {{ queues }}
}
}
#!/bin/sh
# Copyright (c) 2023 Eric Fahlgren <eric.fahlgren@gmail.com>
# SPDX-License-Identifier: GPL-2.0
-# shellcheck disable=SC2039 # "local" not defined in POSIX sh
+# shellcheck disable=SC2039,SC2155 # "local" not defined in POSIX sh
PROG="/usr/bin/snort"
MAIN="/usr/share/snort/main.uc"
{
# From https://forum.openwrt.org/t/snort-3-nfq-with-ips-mode/161172
# https://blog.snort.org/2016/08/running-snort-on-commodity-hardware.html
- # Not needed when running the nft daq as defragmentation is done by the kernel.
+ # Not needed when running the nfq daq as defragmentation is done by the kernel.
# What about pcap?
local filter_method=$(uci -q get snort.snort.method)
setup() {
# Generates all the configuration, then reports the config file for snort.
# Does NOT generate the rules file, you'll need to do 'update-rules' first.
+ local log_dir=$(uci get snort.snort.log_dir)
+ [ ! -e "$log_dir" ] && mkdir -p "$log_dir"
nft_rm_table
print snort > "$CONF"
nft_add_table
[ "$manual" = 1 ] && return 0
[ -n "$QUIET" ] && OUT=/dev/null || OUT=$STDOUT
+ local warn no_rules
+ if [ -n "$VERBOSE" ]; then
+ warn='--warn-all'
+ no_rules=0
+ else
+ warn='-q'
+ no_rules=1
+ fi
+
local test_conf="${CONF_DIR}/test_conf.lua"
- print snort > "${test_conf}" || die "Errors during generation of config."
- if $PROG -T -q --warn-all -c "${test_conf}" 2> $OUT ; then
+ _SNORT_WITHOUT_RULES="$no_rules" print snort > "${test_conf}" || die "Errors during generation of snort config."
+ if $PROG -T $warn -c "${test_conf}" 2> $OUT ; then
rm "${test_conf}"
- return 0
+ else
+ die "Errors in snort config tests. Examine ${test_conf} for issues."
fi
- die "Errors in snort config tests."
+
+ if [ "$(uci -q get snort.snort.method)" = "nfq" ]; then
+ local test_nft="${CONF_DIR}/test_conf.nft"
+ print nftables > "${test_nft}" || die "Errors during generation of nftables config."
+ if nft $VERBOSE --check -f "${test_nft}" ; then
+ rm "${test_nft}"
+ else
+ die "Errors in nftables config tests. Examine ${test_nft} for issues."
+ fi
+ fi
+
}
report() {
die "Logging is not enabled in snort config."
fi
- #if [ -z "$pattern" ]; then
- # die "Provide a valid IP and try again."
- #fi
-
[ "$NLINES" = 0 ] && output="cat" || output="head -n $NLINES"
- # Fix this to use json file.
+ local msg src dst dir
tmp="/tmp/snort.report.$$"
- echo "Intrusions involving ${pattern:-all IPs}"
- grep "\b${pattern}\b" "$log_dir/alert_fast.txt" \
- | sed 's/.*"\([^"]*\)".* \([^ :]*\)[: ].*-> \(.*\)/\1#\2#\3/' > "$tmp"
+ for file in "${log_dir}"/*alert_json.txt; do
+ while read -r line; do
+ eval $(jsonfilter -s "$line" -e 'msg=$.msg' -e 'src=$.src_ap' -e 'dst=$.dst_ap' -e 'dir=$.dir')
+ src=$(echo "$src" | sed 's/:.*$//') # Delete all source ports.
+ dst=$(echo "$dst" | sed 's/:0$//') # Delete unspecified dest port.
+ echo "$msg#$src#$dst#$dir"
+ done < "$file"
+ done | grep -i "$pattern" > "$tmp"
+
+ echo "Events involving ${pattern:-all IPs}"
n_incidents="$(wc -l < $tmp)"
lines=$(sort "$tmp" | uniq -c | sort -nr \
- | awk -F'#' '{printf "%-80s %-12s -> %s\n", $1, $2, $3}')
+ | awk -F'#' '{printf "%-80s %s %-13s -> %s\n", $1, $4, $2, $3}')
echo "$lines" | $output
n_lines=$(echo "$lines" | wc -l)
[ "$NLINES" -gt 0 ] && [ "$NLINES" -lt "$n_lines" ] && echo " ... Only showing $NLINES of $n_lines most frequent incidents."
}
status() {
- echo 'tbd'
+ echo -n 'snort is ' ; service snort status
+ ps w | grep -E 'PID|snort' | grep -v grep
}
teardown
;;
resetup)
- QUIET=1 check || die "The generated snort lua configuration contains errors, not restarting."
+ QUIET=1 check || die "The generated snort lua configuration contains errors, not restarting. Run 'snort-mgr check'"
teardown
setup
;;
Report on incidents. Note this is somewhat experimental, so suggested
improvements are quite welcome.
- pattern = IP or piece of IP or something in the message to filter.
+ pattern = A case-insensitive grep pattern used to filter output.
$0 [-t] update-rules
snort = The snort configuration file, which is a lua script.
nftables = The nftables script used to define the input queues when using
the 'nfq' DAQ.
+ help = Display config file help.
$0 [-q] check
# your lan range, default is '192.168.1.0/24'
# external_net - IP range external to home. Usually 'any', but if you only
# care about true external hosts (trusting all lan devices),
-# then '!$HOMENET' or some specific range
+# then '!$HOME_NET' or some specific range
# mode - 'ids' or 'ips', for detection-only or prevention, respectively
# oinkcode - https://www.snort.org/oinkcodes
# config_dir - Location of the base snort configuration files. Default /etc/snort
# action - 'alert', 'block', 'reject' or 'drop'
# method - 'pcap', 'afpacket' or 'nfq'
# snaplen - int daq.snaplen = 1518: set snap length (same as -s) { 0:65535 }
+# include - User-defined snort configuration, applied at end of generated snort.lua
#
# nfq - https://github.com/snort3/libdaq/blob/master/modules/nfq/README.nfq.md
# queue_maxlen - nfq's '--daq-var queue_maxlen=int'
option action 'alert' # one of [alert, block, drop, reject]
option interface 'eth0' # a string
option snaplen '1518' # 1518 <= x <= 65535
+ option include '' # a path string
config nfq 'nfq'
option queue_count '4' # 1 <= x <= 16
let home_net = snort.home_net == 'any' ? "'any'" : snort.home_net;
let external_net = snort.external_net;
-let line_mode = snort.mode == "ids" ? "tap" : "inline";
+let line_mode = snort.mode == "ids" ? "tap" : "inline";
+let mod_mode = snort.mode == "ids" ? "passive" : "inline";
let inputs = null;
let vars = null;
-- Do not edit, automatically generated. See /usr/share/snort/templates.
-- These must be defined before processing snort.lua
--- The default include '/etc/snort/homenet.lua' must not redefine them.
HOME_NET = [[ {{ home_net }} ]]
-EXTERNAL_NET = '{{ external_net }}'
+EXTERNAL_NET = [[ {{ external_net }} ]]
include('{{ snort.config_dir }}/snort.lua')
{% if (snort.mode == 'ips'): %}
['-Q'] = true,
{% endif %}
- ['--daq'] = {{ snort.method }},
+ ['--daq'] = '{{ snort.method }}',
--['--daq-dir'] = '/usr/lib/daq/',
{% if (snort.method == 'nfq'): %}
['--max-packet-threads'] = {{ nfq.thread_count }},
}
ips = {
- mode = {{ line_mode }},
+ mode = '{{ line_mode }}',
variables = default_variables,
- action_override = {{ snort.action }},
- include = "{{ snort.config_dir }}/" .. RULE_PATH .. '/snort.rules',
+ action_override = '{{ snort.action }}',
+{% if (getenv("_SNORT_WITHOUT_RULES") == "1"): %}
+ -- WARNING: THIS IS A TEST-ONLY CONFIGURATION WITHOUT ANY RULES.
+{% else %}
+ include = '{{ snort.config_dir }}/' .. RULE_PATH .. '/snort.rules',
+{% endif -%}
}
daq = {
modules = {
{
name = '{{ snort.method }}',
- mode = {{ line_mode }},
+ mode = '{{ mod_mode }}',
variables = {{ vars }},
}
}
{% if (int(snort.logging)): %}
-- Note that this is also the location of the PID file, if you use it.
-output.logdir = "{{ snort.log_dir }}"
+output.logdir = '{{ snort.log_dir }}'
--- Maybe add snort.log_type, 'fast', 'json' and 'full'?
--- Json would be best for reporting, see 'snort-mgr report' code.
-- alert_full = { file = true, }
+--[[
alert_fast = {
-- bool alert_fast.file = false: output to alert_fast.txt instead of stdout
-- bool alert_fast.packet = false: output packet dump with alert
file = true,
packet = false,
}
+--]]
+
alert_json = {
-- bool alert_json.file = false: output to alert_json.txt instead of stdout
--- multi alert_json.fields = timestamp pkt_num proto pkt_gen pkt_len dir src_ap dst_ap rule action: selected fields will be output
-- int alert_json.limit = 0: set maximum size in MB before rollover (0 is unlimited) { 0:maxSZ }
-- string alert_json.separator = , : separate fields with this character sequence
+-- multi alert_json.fields = 'timestamp pkt_num proto pkt_gen pkt_len dir src_ap dst_ap'
+-- Rule action: selected fields will be output in given order left to right.
+-- { action | class | b64_data | client_bytes | client_pkts | dir
+-- | dst_addr | dst_ap | dst_port | eth_dst | eth_len | eth_src
+-- | eth_type | flowstart_time | geneve_vni | gid | icmp_code
+-- | icmp_id | icmp_seq | icmp_type | iface | ip_id | ip_len
+-- | msg | mpls | pkt_gen | pkt_len | pkt_num | priority
+-- | proto | rev | rule | seconds | server_bytes | server_pkts
+-- | service | sgt | sid | src_addr | src_ap | src_port | target
+-- | tcp_ack | tcp_flags | tcp_len | tcp_seq | tcp_win | timestamp
+-- | tos | ttl | udp_len | vlan }
+
+-- This is a minimal set of fields that simply supports 'snort-mgr report'
+-- and minimizes log size:
+ fields = 'dir src_ap dst_ap msg',
+
+-- This set also supports the report, but closely matches 'alert_fast' contents.
+--fields = 'timestamp pkt_num proto pkt_gen pkt_len dir src_ap dst_ap rule action msg',
+
file = true,
}
+--[[
+unified2 = {
+ limit = 10, -- int unified2.limit = 0: set maximum size in MB before rollover (0 is unlimited) { 0:maxSZ }
+}
+--]]
+
{% endif -%}
normalizer = {
app_stats_period = 60,
}
{% endif %}
+
+{%
+if (snort.include) {
+ // We use the ucode include here, so that the included file is also
+ // part of the template and can use values passed in from the config.
+ printf("-- The following content from included file '%s'\n", snort.include);
+ include(snort.include, { snort, nfq });
+}
+%}