aboutsummaryrefslogtreecommitdiff
path: root/net/snort3/files/main.uc
diff options
context:
space:
mode:
authorEric Fahlgren <ericfahlgren@gmail.com>2023-11-27 08:21:43 -0800
committerRosen Penev <rosenp@gmail.com>2023-12-03 13:53:58 -0800
commitf21dffc2a306ad97cefa03dac8bcee0552da556f (patch)
treeff687532e830989715d5a324fcc02446250626d3 /net/snort3/files/main.uc
parent904438be39d2fb80573ab45ce606a20bb4fbe7e1 (diff)
snort3: complete rework
- Add many options to config file. - Move rules and generated snort.lua to /tmp. - Add script for downloading rules. - Add preliminary reporting capabilites. Signed-off-by: Eric Fahlgren <ericfahlgren@gmail.com>
Diffstat (limited to 'net/snort3/files/main.uc')
-rw-r--r--net/snort3/files/main.uc263
1 files changed, 263 insertions, 0 deletions
diff --git a/net/snort3/files/main.uc b/net/snort3/files/main.uc
new file mode 100644
index 000000000..7db420f33
--- /dev/null
+++ b/net/snort3/files/main.uc
@@ -0,0 +1,263 @@
+{%
+//------------------------------------------------------------------------------
+// Copyright (c) 2023 Eric Fahlgren <eric.fahlgren@gmail.com>
+// SPDX-License-Identifier: GPL-2.0
+//
+// The tables defined using 'config_item' are the source of record for the
+// configuration file, '/etc/config/snort'. If you wish to add new items,
+// do that only in the tables and propagate that use into the templates.
+//
+//------------------------------------------------------------------------------
+
+import { cursor } from 'uci';
+let uci = cursor();
+
+function wrn(fmt, ...args) {
+ if (getenv("QUIET"))
+ exit(1);
+
+ let msg = "ERROR: " + sprintf(fmt, ...args);
+
+ if (getenv("TTY"))
+ warn(`\033[33m${msg}\033[m\n`);
+ else
+ warn(`[!] ${msg}\n`);
+ exit(1);
+}
+
+//------------------------------------------------------------------------------
+
+function config_item(type, values, def) {
+ // If no default value is provided explicity, then values[0] is used as default.
+ if (! type in [ "enum", "range", "path", "str" ]) {
+ wrn(`Invalid item type '${type}', must be one of "enum", "range", "path" or "str".`);
+ return;
+ }
+ if (type == "range" && (length(values) != 2 || values[0] > values[1])) {
+ wrn(`A 'range' type item must have exactly 2 values in ascending order.`);
+ return;
+ }
+ // Maybe check paths for existence???
+
+ return {
+ type: type,
+ values: values,
+ default: def ?? values[0],
+
+ contains: function(value) {
+ // Check if the value is contained in the listed values,
+ // depending on the item type.
+ switch (this.type) {
+ case "enum":
+ return value in this.values;
+ case "range":
+ return value >= this.values[0] && value <= this.values[1];
+ default:
+ return true;
+ }
+ },
+
+ allowed: function() {
+ // Show a pretty version of the possible values, for error messages.
+ switch (this.type) {
+ case "enum":
+ return "one of [" + join(", ", this.values) + "]";
+ case "range":
+ return `${this.values[0]} <= x <= ${this.values[1]}`;
+ case "path":
+ return "a path string";
+ case "str":
+ return "a string";
+ default:
+ return "???";
+ }
+ },
+ }
+};
+
+const snort_config = {
+ enabled: config_item("enum", [ 0, 1 ], 0), // Defaults to off, so that user must configure before first start.
+ manual: config_item("enum", [ 0, 1 ], 1), // Allow user to manually configure, legacy behavior when enabled.
+ oinkcode: config_item("str", [ "" ]), // User subscription oinkcode. Much more in 'snort-rules' script.
+ home_net: config_item("str", [ "" ], "192.168.1.0/24"),
+ external_net: config_item("str", [ "" ], "any"),
+
+ config_dir: config_item("path", [ "/etc/snort" ]), // Location of the base snort configuration files.
+ temp_dir: config_item("path", [ "/var/snort.d" ]), // Location of all transient snort config, including downloaded rules.
+ log_dir: config_item("path", [ "/var/log" ]), // Location of the generated logs, and oh-by-the-way the snort PID file (why?).
+ logging: config_item("enum", [ 0, 1 ], 1),
+ openappid: config_item("enum", [ 0, 1 ], 0),
+
+ mode: config_item("enum", [ "ids", "ips" ]),
+ method: config_item("enum", [ "pcap", "afpacket", "nfq" ]),
+ 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 }
+};
+
+const nfq_config = {
+ queue_count: config_item("range", [ 1, 16 ], 4), // Count of queues to allocate in nft chain when method=nfq, usually 2-8.
+ queue_start: config_item("range", [ 1, 32768], 4), // Start of queue numbers in nftables.
+ queue_maxlen: config_item("range", [ 1024, 65536 ], 1024), // --daq-var queue_maxlen=int
+ fanout_type: config_item("enum", [ "hash", "lb", "cpu", "rollover", "rnd", "qm"], "hash"), // See below.
+ thread_count: config_item("range", [ 0, 32 ], 0), // 0 = use cpu count
+ chain_type: config_item("enum", [ "prerouting", "input", "forward", "output", "postrouting" ], "input"),
+ chain_priority: config_item("enum", [ "raw", "filter", "300"], "filter"),
+ include: config_item("path", [ "" ]), // User-defined rules to include inside queue chain.
+};
+
+
+let _snort_config_doc =
+"
+This is not an exhaustive list of configuration items, just those that
+require more explanation than is given in the tables that define them, below.
+
+https://openwrt.org/docs/guide-user/services/snort
+
+snort
+ manual - When set to 1, use manual configuration for legacy behavior.
+ When disabled, then use this config.
+ interface - Default should usually be 'uci get network.wan.device',
+ something like 'eth0'
+ home_net - IP range/ranges to protect. May be 'any', but more likely it's
+ 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
+ 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
+ temp_dir - Location of all transient snort config, including downloaded rules
+ Default /var/snort.d
+ logging - Enable external logging of events thus enabling 'snort-mgr report',
+ otherwise events only go to system log (i.e., 'logread -e snort:')
+ log_dir - Location of the generated logs, and oh-by-the-way the snort
+ PID file (why?). Default /var/log
+ openappid - Enabled inspection using the 'openappid' package
+ See 'opkg info openappid'
+ action - 'alert', 'block', 'reject' or 'drop'
+ method - 'pcap', 'afpacket' or 'nfq'
+ snaplen - int daq.snaplen = 1518: set snap length (same as -s) { 0:65535 }
+
+nfq - https://github.com/snort3/libdaq/blob/master/modules/nfq/README.nfq.md
+ queue_maxlen - nfq's '--daq-var queue_maxlen=int'
+ queue_count - Count of queues to use when method=nfq, usually 2-8
+ fanout_type - Sets kernel load balancing algorithm*, one of hash, lb, cpu,
+ rollover, rnd, qm.
+ thread_count - int snort.-z: <count> maximum number of packet threads
+ (same as --max-packet-threads); 0 gets the number of
+ CPU cores reported by the system; default is 1 { 0:max32 }
+ chain_type - Chain type when generating nft output
+ chain_priority - Chain priority when generating nft output
+ include - Full path to user-defined extra rules to include inside queue chain
+
+ * - for details on fanout_type, see these pages:
+ https://github.com/florincoras/daq/blob/master/README
+ https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt
+";
+
+function snort_config_doc(comment) {
+ if (comment == null) comment = "";
+ if (comment != "") comment += " ";
+ for (let line in split(_snort_config_doc, "\n")) {
+ let msg = rtrim(sprintf("%s%s", comment, line));
+ print(msg, "\n");
+ }
+}
+
+//------------------------------------------------------------------------------
+
+function load(section, config) {
+ let self = {
+ ".name": section,
+ ".config": config,
+ };
+
+ // Set the defaults from definitions in table.
+ for (let item in config) {
+ self[item] = config[item].default;
+ }
+
+ // Overwrite them with any uci config settings.
+ let cfg = uci.get_all("snort", section);
+ for (let item in cfg) {
+ // If you need to rename, delete or change the meaning of a
+ // config item, just intercept it and do the work here.
+
+ if (exists(config, item)) {
+ let val = cfg[item];
+ if (config[item].contains(val))
+ self[item] = val;
+ else {
+ wrn(`In option ${item}='${val}', must be ${config[item].allowed()}`);
+ // ??? self[item] = config[item][0]; ???
+ }
+ }
+ }
+
+ return self;
+}
+
+let snort = null;
+let nfq = null;
+function load_all() {
+ snort = load("snort", snort_config);
+ nfq = load("nfq", nfq_config);
+}
+
+function dump_config(settings) {
+ let section = settings[".name"];
+ let config = settings[".config"];
+ printf("config %s '%s'\n", section, section);
+ for (let item in config) {
+ printf("\toption %-15s %-17s# %s\n", item, `'${settings[item]}'`, config[item].allowed());
+ }
+ print("\n");
+}
+
+function render_snort() {
+ include("templates/snort.uc", { snort, nfq });
+}
+
+function render_nftables() {
+ include("templates/nftables.uc", { snort, nfq });
+}
+
+function render_config() {
+ snort_config_doc("#");
+ dump_config(snort);
+ dump_config(nfq);
+}
+
+function render_help() {
+ snort_config_doc();
+}
+
+//------------------------------------------------------------------------------
+
+load_all();
+
+switch (getenv("TYPE")) {
+ case "snort":
+ render_snort();
+ return;
+
+ case "nftables":
+ render_nftables();
+ return;
+
+ case "config":
+ render_config();
+ return;
+
+ case "help":
+ render_help();
+ return;
+
+ default:
+ print("Invalid table type.\n");
+ return;
+}
+
+//------------------------------------------------------------------------------
+-%}