{% //------------------------------------------------------------------------------ // Copyright (c) 2023 Eric Fahlgren // 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: 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; } //------------------------------------------------------------------------------ -%}