snort3: add missing config include and general cleanup
[feed/packages.git] / net / snort3 / files / snort-mgr
1 #!/bin/sh
2 # Copyright (c) 2023 Eric Fahlgren <eric.fahlgren@gmail.com>
3 # SPDX-License-Identifier: GPL-2.0
4 # shellcheck disable=SC2039,SC2155 # "local" not defined in POSIX sh
5
6 PROG="/usr/bin/snort"
7 MAIN="/usr/share/snort/main.uc"
8 CONF_DIR="/var/snort.d"
9 CONF="${CONF_DIR}/snort_conf.lua"
10
11 VERBOSE=
12 TESTING=
13 NLINES=0
14
15 [ ! -e "$CONF_DIR" ] && mkdir "$CONF_DIR"
16 [ -e /dev/stdin ] && STDIN=/dev/stdin || STDIN=/proc/self/fd/0
17 [ -e /dev/stdout ] && STDOUT=/dev/stdout || STDOUT=/proc/self/fd/1
18 [ -t 2 ] && export TTY=1
19
20 die() {
21 [ -n "$QUIET" ] || echo "$@" >&2
22 exit 1
23 }
24
25 disable_offload()
26 {
27 # From https://forum.openwrt.org/t/snort-3-nfq-with-ips-mode/161172
28 # https://blog.snort.org/2016/08/running-snort-on-commodity-hardware.html
29 # Not needed when running the nfq daq as defragmentation is done by the kernel.
30 # What about pcap?
31
32 local filter_method=$(uci -q get snort.snort.method)
33 if [ "$filter_method" = "afpacket" ]; then
34 local wan=$(uci get snort.snort.interface)
35 if [ -n "$wan" ] && ethtool -k "$wan" | grep -q -E '(tcp-segmentation-offload|receive-offload): on' ; then
36 ethtool -K "$wan" gro off lro off tso off 2> /dev/null
37 log "Disabled gro, lro and tso on '$wan' using ethtool."
38 fi
39 fi
40 }
41
42 nft_rm_table() {
43 for table_type in 'inet' 'netdev'; do
44 nft list tables | grep -q "${table_type} snort" && nft delete table "${table_type}" snort
45 done
46 }
47
48 nft_add_table() {
49 if [ "$(uci -q get snort.snort.method)" = "nfq" ]; then
50 print nftables | nft $VERBOSE -f $STDIN
51 [ -n "$VERBOSE" ] && nft list table inet snort
52 fi
53 }
54
55 setup() {
56 # Generates all the configuration, then reports the config file for snort.
57 # Does NOT generate the rules file, you'll need to do 'update-rules' first.
58 local log_dir=$(uci get snort.snort.log_dir)
59 [ ! -e "$log_dir" ] && mkdir -p "$log_dir"
60 nft_rm_table
61 print snort > "$CONF"
62 nft_add_table
63 echo "$CONF"
64 }
65
66 teardown() {
67 # Merely cleans up after.
68 nft_rm_table
69 [ -e "$CONF" ] && rm "$CONF"
70 }
71
72 update_rules() {
73 /usr/bin/snort-rules $TESTING
74 }
75
76 print() {
77 # '$1' is file type to generate, one of:
78 # config, snort or nftables
79 TYPE=$1 utpl -S "$MAIN"
80 }
81
82 check() {
83 local manual=$(uci get snort.snort.manual)
84 [ "$manual" = 1 ] && return 0
85
86 [ -n "$QUIET" ] && OUT=/dev/null || OUT=$STDOUT
87 local warn no_rules
88 if [ -n "$VERBOSE" ]; then
89 warn='--warn-all'
90 no_rules=0
91 else
92 warn='-q'
93 no_rules=1
94 fi
95
96 local test_conf="${CONF_DIR}/test_conf.lua"
97 _SNORT_WITHOUT_RULES="$no_rules" print snort > "${test_conf}" || die "Errors during generation of snort config."
98 if $PROG -T $warn -c "${test_conf}" 2> $OUT ; then
99 rm "${test_conf}"
100 else
101 die "Errors in snort config tests. Examine ${test_conf} for issues."
102 fi
103
104 if [ "$(uci -q get snort.snort.method)" = "nfq" ]; then
105 local test_nft="${CONF_DIR}/test_conf.nft"
106 print nftables > "${test_nft}" || die "Errors during generation of nftables config."
107 if nft $VERBOSE --check -f "${test_nft}" ; then
108 rm "${test_nft}"
109 else
110 die "Errors in nftables config tests. Examine ${test_nft} for issues."
111 fi
112 fi
113
114 }
115
116 report() {
117 # Reported IPs have source port stripped, but destination port (if any)
118 # retained.
119 #
120 # json notes
121 # from alert_fast:
122 # 08/30-11:39:57.639021 [**] [1:382:11] "PROTOCOL-ICMP PING Windows" [**] [Classification: Misc activity] [Priority: 3] {ICMP} 10.1.1.186 -> 10.1.1.20
123 #
124 # same event in alert_json (single line broken for clarity):
125 # { "timestamp" : "08/30-11:39:57.639021", "pkt_num" : 5366, "proto" : "ICMP", "pkt_gen" : "raw",
126 # "pkt_len" : 60, "dir" : "C2S", "src_ap" : "10.1.1.186:0", "dst_ap" : "10.1.1.20:0",
127 # "rule" : "1:382:11", "action" : "allow" }
128 #
129 # Second part of "rule", 382, is "sid" in ruleset, suffixing 11 is "rev".
130 # grep '\bsid:382\b' /etc/snort/rules/snort.rules (again, single line broken for clarity):
131 # alert icmp $EXTERNAL_NET any -> $HOME_NET any ( msg:"PROTOCOL-ICMP PING Windows";
132 # itype:8; content:"abcdefghijklmnop",depth 16; metadata:ruleset community;
133 # classtype:misc-activity; sid:382; rev:11; )
134 #
135 # Not sure where the prefixing 1 comes from.
136
137 local logging=$(uci get snort.snort.logging)
138 local log_dir=$(uci get snort.snort.log_dir)
139 local pattern="$1"
140
141 if [ "$logging" = 0 ]; then
142 die "Logging is not enabled in snort config."
143 fi
144
145 [ "$NLINES" = 0 ] && output="cat" || output="head -n $NLINES"
146
147 local msg src dst dir
148 tmp="/tmp/snort.report.$$"
149 for file in "${log_dir}"/*alert_json.txt; do
150 while read -r line; do
151 eval $(jsonfilter -s "$line" -e 'msg=$.msg' -e 'src=$.src_ap' -e 'dst=$.dst_ap' -e 'dir=$.dir')
152 src=$(echo "$src" | sed 's/:.*$//') # Delete all source ports.
153 dst=$(echo "$dst" | sed 's/:0$//') # Delete unspecified dest port.
154 echo "$msg#$src#$dst#$dir"
155 done < "$file"
156 done | grep -i "$pattern" > "$tmp"
157
158 echo "Events involving ${pattern:-all IPs}"
159 n_incidents="$(wc -l < $tmp)"
160 lines=$(sort "$tmp" | uniq -c | sort -nr \
161 | awk -F'#' '{printf "%-80s %s %-13s -> %s\n", $1, $4, $2, $3}')
162 echo "$lines" | $output
163 n_lines=$(echo "$lines" | wc -l)
164 [ "$NLINES" -gt 0 ] && [ "$NLINES" -lt "$n_lines" ] && echo " ... Only showing $NLINES of $n_lines most frequent incidents."
165 printf "%7d total incidents\n" "$n_incidents"
166 rm "$tmp"
167 }
168
169 status() {
170 echo -n 'snort is ' ; service snort status
171 ps w | grep -E 'PID|snort' | grep -v grep
172 }
173
174
175 while [ -n "$1" ]; do
176 case "$1" in
177 -q)
178 export QUIET=1
179 shift
180 ;;
181 -v)
182 export VERBOSE=-e
183 shift
184 ;;
185 -t)
186 TESTING=-t
187 shift
188 ;;
189 -n)
190 NLINES="$2"
191 shift
192 shift
193 ;;
194 *)
195 break
196 ;;
197 esac
198 done
199
200 case "$1" in
201 setup)
202 setup
203 ;;
204 teardown)
205 teardown
206 ;;
207 resetup)
208 QUIET=1 check || die "The generated snort lua configuration contains errors, not restarting. Run 'snort-mgr check'"
209 teardown
210 setup
211 ;;
212 update-rules)
213 update_rules
214 ;;
215 check)
216 check
217 ;;
218 print)
219 print "$2"
220 ;;
221 report)
222 report "$2"
223 ;;
224 status)
225 status
226 ;;
227 *)
228 cat <<USAGE
229 Usage:
230
231 -n = show only NLINES of output
232 -q = quiet
233 -v = verbose
234 -t = testing mode
235
236 $0 [-v] [-q] setup|teardown|resetup
237
238 Normally only used internally by init scripts to manage the generation
239 of configuration files and any needed firewall rules. None of these
240 modify the snort rules in any way (see 'update-rules').
241 setup = generates snort config, sets up firewall.
242 teardown = removes any firewall rules.
243 resetup = shorthand for teardown and then setup.
244
245
246 $0 [-n lines] report [pattern]
247
248 Report on incidents. Note this is somewhat experimental, so suggested
249 improvements are quite welcome.
250 pattern = A case-insensitive grep pattern used to filter output.
251
252 $0 [-t] update-rules
253
254 Download and install the snort ruleset. Testing mode generates a canned
255 rule that matches IPv4 ping requests. A typical test scenario might look
256 like:
257
258 > snort-mgr -t update-rules
259 > /etc/init.d/snort start
260 > ping -c4 8.8.8.8
261 > logread -e "TEST ALERT"
262
263
264 $0 print config|snort|nftables
265
266 Print the rendered file contents.
267 config = Display contents of /etc/config/snort, but with all values and
268 descriptions. Missing values shown with defaults.
269 snort = The snort configuration file, which is a lua script.
270 nftables = The nftables script used to define the input queues when using
271 the 'nfq' DAQ.
272 help = Display config file help.
273
274
275 $0 [-q] check
276
277 Test the rendered config using snort's check mode without
278 applying it to the running system.
279
280
281 $0 status
282
283 Print the nfq counter values and blah blah blah
284
285 USAGE
286 ;;
287 esac