snort3: add missing config include and general cleanup
- Delete legacy configuration files homenet.lua and local.lua - Add snort config 'include' to allow user customizations in the lua - Enhance 'check' to test generated nftables file - Suppress inclusion of rules file when doing silent config check - Suppress warnings on configuration check unless '-v'erbose - Replace text logging with json logging to reduce footprint and make reports easier - Fix some typos in the snort.uc template - Fix up some error messages suggesting solutions Signed-off-by: Eric Fahlgren <ericfahlgren@gmail.com>
This commit is contained in:
parent
880ac1f3d5
commit
0d2dac8792
8 changed files with 120 additions and 113 deletions
|
@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
|||
|
||||
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/
|
||||
|
@ -125,15 +125,12 @@ define Package/snort3/install
|
|||
$(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
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
-- 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"
|
|
@ -1,62 +0,0 @@
|
|||
-- 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,
|
||||
--}
|
|
@ -93,6 +93,8 @@ const snort_config = {
|
|||
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 = {
|
||||
|
@ -123,7 +125,7 @@ snort
|
|||
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
|
||||
|
@ -138,6 +140,7 @@ 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'
|
||||
|
@ -237,7 +240,8 @@ function render_help() {
|
|||
|
||||
load_all();
|
||||
|
||||
switch (getenv("TYPE")) {
|
||||
let table_type = getenv("TYPE");
|
||||
switch (table_type) {
|
||||
case "snort":
|
||||
render_snort();
|
||||
return;
|
||||
|
@ -255,7 +259,7 @@ switch (getenv("TYPE")) {
|
|||
return;
|
||||
|
||||
default:
|
||||
print("Invalid table type.\n");
|
||||
print(`Invalid table type '${table_type}', should be one of snort, nftables, config, help.\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,13 @@ table inet snort {
|
|||
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 }}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/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"
|
||||
|
@ -26,7 +26,7 @@ disable_offload()
|
|||
{
|
||||
# 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)
|
||||
|
@ -55,6 +55,8 @@ nft_add_table() {
|
|||
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
|
||||
|
@ -82,13 +84,33 @@ check() {
|
|||
[ "$manual" = 1 ] && return 0
|
||||
|
||||
[ -n "$QUIET" ] && OUT=/dev/null || OUT=$STDOUT
|
||||
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
|
||||
rm "${test_conf}"
|
||||
return 0
|
||||
local warn no_rules
|
||||
if [ -n "$VERBOSE" ]; then
|
||||
warn='--warn-all'
|
||||
no_rules=0
|
||||
else
|
||||
warn='-q'
|
||||
no_rules=1
|
||||
fi
|
||||
die "Errors in snort config tests."
|
||||
|
||||
local test_conf="${CONF_DIR}/test_conf.lua"
|
||||
_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}"
|
||||
else
|
||||
die "Errors in snort config tests. Examine ${test_conf} for issues."
|
||||
fi
|
||||
|
||||
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() {
|
||||
|
@ -120,20 +142,23 @@ 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."
|
||||
|
@ -142,7 +167,8 @@ report() {
|
|||
}
|
||||
|
||||
status() {
|
||||
echo 'tbd'
|
||||
echo -n 'snort is ' ; service snort status
|
||||
ps w | grep -E 'PID|snort' | grep -v grep
|
||||
}
|
||||
|
||||
|
||||
|
@ -179,7 +205,7 @@ case "$1" in
|
|||
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
|
||||
;;
|
||||
|
@ -221,7 +247,7 @@ Usage:
|
|||
|
||||
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
|
||||
|
||||
|
@ -243,6 +269,7 @@ Usage:
|
|||
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
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
# 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
|
||||
|
@ -28,6 +28,7 @@
|
|||
# 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'
|
||||
|
@ -61,6 +62,7 @@ config snort 'snort'
|
|||
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
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
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;
|
||||
|
@ -32,9 +33,8 @@ case "nfq":
|
|||
-- 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')
|
||||
|
||||
|
@ -42,7 +42,7 @@ snort = {
|
|||
{% 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 }},
|
||||
|
@ -50,10 +50,14 @@ snort = {
|
|||
}
|
||||
|
||||
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 = {
|
||||
|
@ -63,7 +67,7 @@ daq = {
|
|||
modules = {
|
||||
{
|
||||
name = '{{ snort.method }}',
|
||||
mode = {{ line_mode }},
|
||||
mode = '{{ mod_mode }}',
|
||||
variables = {{ vars }},
|
||||
}
|
||||
}
|
||||
|
@ -75,12 +79,11 @@ alert_syslog = {
|
|||
|
||||
{% 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
|
||||
|
@ -88,14 +91,40 @@ alert_fast = {
|
|||
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 = {
|
||||
|
@ -124,3 +153,12 @@ appid = {
|
|||
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 });
|
||||
}
|
||||
%}
|
||||
|
|
Loading…
Reference in a new issue