1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
#!/bin/sh
# Copyright (c) 2023-2024 Eric Fahlgren <eric.fahlgren@gmail.com>
# SPDX-License-Identifier: GPL-2.0
# shellcheck disable=SC2039,SC2155 # "local" not defined in POSIX sh
set -o nounset
alias log='logger -s -t "snort-rules[$$]" -p "info"'
download_rules() {
# Further information:
# https://www.snort.org/products#rule_subscriptions
# https://www.snort.org/oinkcodes
#
# Also, what to do about "subscription" vs Talos_LightSPD rules when subbed?
# Add a "use_rules" list or option or something?
local oinkcode=$(uci -q get snort.snort.oinkcode)
local conf_dir=$(uci -q get snort.snort.config_dir || echo "/etc/snort")
local rules_dir="$conf_dir/rules"
local data_dir=$(uci -q get snort.snort.temp_dir || echo "/var/snort.d")
local data_tar="$data_dir/rules.tar.gz"
local new_rules
local rules_file
local archive_loc
# Make sure everything exists.
[ -d "$data_dir" ] || mkdir -p "$data_dir"
if $testing ; then
log "Generating testing rules..."
archive_loc="testing-rules"
new_rules="$data_dir/$archive_loc"
rm -fr "${new_rules:?}"
mkdir -p "$new_rules"
rules_file="$new_rules/testing.rules"
{
echo 'alert icmp any any <> any any (msg:"TEST ALERT ICMP v4"; icode:0; itype: 8; sid:99010;)'
echo 'alert icmp any any <> any any (msg:"TEST ALERT ICMP v6"; icode:0; itype:33; sid:99011;)'
echo 'alert icmp any any <> any any (msg:"TEST ALERT ICMP v6"; icode:0; itype:34; sid:99012;)'
} >> "$rules_file"
else
if [ -z "$oinkcode" ]; then
# If you do not have a subscription, then we use the community rules:
log "Downloading community rules..."
url="https://www.snort.org/downloads/community/snort3-community-rules.tar.gz"
archive_loc="snort3-community-rules"
else
# If you have a subscription and its corresponding oinkcode, use this:
#
# 'snortver' is the version number of the snort executable in use on your
# router.
#
# Ideally, the 'snort --version' output would work, but OpenWrt builds
# are often between (or, more likely, newer than) those listed on the
# snort.org downloads page.
#
# So instead, we define it manually to be the value just before the
# installed version. Look on https://www.snort.org/advisories/ and
# select the most recent date. On that page, find the closest version
# number preceding your installed version and modify the hard-coded
# value below (for example, installed is 31600 then use 31470):
#snortver=$(snort --version | awk '/Version/ {print gensub("\\.", "", "", $NF)}')
snortver=31470
log "Downloading subscription rules..."
url="https://www.snort.org/rules/snortrules-snapshot-$snortver.tar.gz?oinkcode=$oinkcode"
# Non-community tar contains many "*.rules" file, we only care about
# the one directory.
archive_loc="rules"
fi
wget "$url" -O "$data_tar" 2>&1 | log || exit 1
old_rules="$data_dir/old.rules"
if $backup; then
rm -fr "${old_rules:?}"
mkdir -p "$old_rules"
for rules_file in "$rules_dir"/*; do
# Before we overwrite with the new download.
log "Stashing '$rules_file' to '$old_rules/'..."
mv -f "$rules_file" "$old_rules/"
done
fi
log "Unpacking '$data_tar'..."
tar xzvof "$data_tar" "$archive_loc" -C "$data_dir" | log || exit 1
# Get rid of the non-rule files and aggregator.
new_rules="$data_dir/$archive_loc"
find "$new_rules" \( -iname 'includes.rules' -o ! -iname '*.rules' -type f \) -exec rm '{}' \;
# Old unfinished experiment with diffing old and new rules.
#for rules_file in "$new_rules"/*; do
#blah blah
#if [ -e "$old_rules" ] && ! cmp -s "$new_rules" "$old_rules" ; then
# diff "$new_rules" "$old_rules" 2>&1 | log
#fi
fi
mkdir -p "$conf_dir"
rm -fr "${rules_dir:?}"
if $persist; then
mv -f "$new_rules" "$rules_dir"
else
ln -s "$new_rules" "$rules_dir"
fi
log "Snort rules loaded, restart snort now."
}
#-------------------------------------------------------------------------------
testing=false
persist=false
backup=false
usage() {
local msg="$1"
[ -n "$msg" ] && printf "ERROR: %s\n\n" "$msg"
cat <<USAGE
Usage:
$0 [-b/--backup] [-t/--testing] [-p/--persist]
-b = Attempt to copy current rules to '\$temp_dir/old.rules/' before
installing new rules.
-t = Don't download any rules, instead create synthetic testing rules.
-p = Move the downloaded rules to '\$conf_dir/rules/' (usually '/etc/snort/'),
so that they persist across reboots and sysupgrades. If you do not
specify this option, then the rules are stored in '\$temp_dir', and a
symbolic link is created to them.
After running 'snort-rules', you should run 'snort-mgr check -v' to verify
that there are no errors.
USAGE
exit 1
}
while [ "${1:-}" ]; do
case "$1" in
-h|--help)
usage
;;
-b|--backup)
backup=true
;;
-t|--testing)
testing=true
;;
-p|--persist)
persist=true
;;
*)
usage "'$1' is not a valid option"
;;
esac
shift
done
download_rules
|