diff options
author | Eric Fahlgren <ericfahlgren@gmail.com> | 2023-11-27 08:21:43 -0800 |
---|---|---|
committer | Rosen Penev <rosenp@gmail.com> | 2023-12-03 13:53:58 -0800 |
commit | f21dffc2a306ad97cefa03dac8bcee0552da556f (patch) | |
tree | ff687532e830989715d5a324fcc02446250626d3 /net/snort3/files/main.uc | |
parent | 904438be39d2fb80573ab45ce606a20bb4fbe7e1 (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.uc | 263 |
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; +} + +//------------------------------------------------------------------------------ +-%} |