diff --git a/security/q-feeds-connector/Makefile b/security/q-feeds-connector/Makefile index dffd163a2..295ca12f0 100644 --- a/security/q-feeds-connector/Makefile +++ b/security/q-feeds-connector/Makefile @@ -1,5 +1,5 @@ PLUGIN_NAME= q-feeds-connector -PLUGIN_VERSION= 1.0 +PLUGIN_VERSION= 1.1 PLUGIN_COMMENT= Connector for Q-Feeds threat intel PLUGIN_MAINTAINER= devel@qfeeds.com diff --git a/security/q-feeds-connector/pkg-descr b/security/q-feeds-connector/pkg-descr index c6c57300e..345782502 100644 --- a/security/q-feeds-connector/pkg-descr +++ b/security/q-feeds-connector/pkg-descr @@ -3,6 +3,13 @@ Connector for Q-Feeds threat intel Plugin Changelog ================ +1.1 + +* remove QfeedsStatus as the new table defaults are different +* add unbound blocklist support +* Events: fix empty interface names + + 1.0 * Intial release version diff --git a/security/q-feeds-connector/src/opnsense/mvc/app/controllers/OPNsense/QFeeds/Api/SettingsController.php b/security/q-feeds-connector/src/opnsense/mvc/app/controllers/OPNsense/QFeeds/Api/SettingsController.php index ed8a3a27b..b65b7b1a6 100644 --- a/security/q-feeds-connector/src/opnsense/mvc/app/controllers/OPNsense/QFeeds/Api/SettingsController.php +++ b/security/q-feeds-connector/src/opnsense/mvc/app/controllers/OPNsense/QFeeds/Api/SettingsController.php @@ -73,7 +73,7 @@ class SettingsController extends ApiMutableModelControllerBase $ifnames = []; foreach (Config::getInstance()->object()->interfaces->children() as $key => $node) { if (!empty((string)$node->if)) { - $ifnames[(string)$node->if] = (string)($node->descr ?? strtoupper($key)); + $ifnames[(string)$node->if] = !empty((string)($node->descr)) ? (string)($node->descr) : strtoupper($key); } } $data = json_decode((new Backend())->configdRun('qfeeds logs') ?? '[]', true); diff --git a/security/q-feeds-connector/src/opnsense/mvc/app/controllers/OPNsense/QFeeds/forms/settings.xml b/security/q-feeds-connector/src/opnsense/mvc/app/controllers/OPNsense/QFeeds/forms/settings.xml index 742ceb3cc..0469ed6ca 100644 --- a/security/q-feeds-connector/src/opnsense/mvc/app/controllers/OPNsense/QFeeds/forms/settings.xml +++ b/security/q-feeds-connector/src/opnsense/mvc/app/controllers/OPNsense/QFeeds/forms/settings.xml @@ -9,4 +9,10 @@ text click here]]> + + connect.general.enable_unbound_bl + + checkbox + Use domain feeds in Unbound DNS blocklist, requires blocklists to be enabled in order to have effect + diff --git a/security/q-feeds-connector/src/opnsense/mvc/app/library/OPNsense/System/Status/QfeedsStatus.php b/security/q-feeds-connector/src/opnsense/mvc/app/library/OPNsense/System/Status/QfeedsStatus.php deleted file mode 100644 index 5477729d6..000000000 --- a/security/q-feeds-connector/src/opnsense/mvc/app/library/OPNsense/System/Status/QfeedsStatus.php +++ /dev/null @@ -1,61 +0,0 @@ -internalPriority = 2; - $this->internalPersistent = true; - $this->internalIsBanner = true; - $this->internalTitle = gettext('QFeeds'); - $this->internalScope = [ - '/ui/q_feeds/' - ]; - } - - public function collectStatus() - { - $cnf = Config::getInstance()->object(); - if (!empty($cnf->system->maximumtableentries) && $cnf->system->maximumtableentries >= 2000000) { - return; - } - $this->internalStatus = SystemStatusCode::ERROR; - $this->internalMessage = gettext( - 'QFeeds requires additional memory to be reserved for aliases. ' . - 'Please increase `Firewall Maximum Table Entries` in `Firewall: Settings: Advanced` to at least' . - ' 2 million items.' - ); - } -} diff --git a/security/q-feeds-connector/src/opnsense/mvc/app/models/OPNsense/QFeeds/Connector.xml b/security/q-feeds-connector/src/opnsense/mvc/app/models/OPNsense/QFeeds/Connector.xml index 9aeebb308..fb158adcf 100644 --- a/security/q-feeds-connector/src/opnsense/mvc/app/models/OPNsense/QFeeds/Connector.xml +++ b/security/q-feeds-connector/src/opnsense/mvc/app/models/OPNsense/QFeeds/Connector.xml @@ -5,6 +5,7 @@ + diff --git a/security/q-feeds-connector/src/opnsense/scripts/qfeeds/lib/__init__.py b/security/q-feeds-connector/src/opnsense/scripts/qfeeds/lib/__init__.py index bf4c528aa..f0f029c1d 100644 --- a/security/q-feeds-connector/src/opnsense/scripts/qfeeds/lib/__init__.py +++ b/security/q-feeds-connector/src/opnsense/scripts/qfeeds/lib/__init__.py @@ -45,6 +45,7 @@ class QFeedsActions: 'fetch', 'show_index', 'firewall_load', + 'unbound_load', 'update', 'stats', 'logs' @@ -113,6 +114,13 @@ class QFeedsActions: ) yield 'load feed %s [%s]' % (feed['feed_type'], sp.stderr.strip().replace("\n", " ")) + def unbound_load(self): + bl_conf = '/usr/local/etc/unbound/qfeeds-blocklists.conf' + if os.path.exists(bl_conf) and os.path.getsize(bl_conf) > 20: + # when qfeeds-blocklists.conf is ~empty, skip updates + subprocess.run(['/usr/local/sbin/configctl', 'unbound', 'dnsbl']) + yield 'update unbound blocklist' + def update(self): update_sleep = 99999 try: @@ -128,7 +136,7 @@ class QFeedsActions: if do_update: if 0 < update_sleep <= 300: time.sleep(update_sleep) - for action in ['fetch_index', 'fetch', 'firewall_load']: + for action in ['fetch_index', 'fetch', 'firewall_load', 'unbound_load']: yield from getattr(self, action)() def stats(self): diff --git a/security/q-feeds-connector/src/opnsense/scripts/unbound/blocklists/qfeeds_bl.py b/security/q-feeds-connector/src/opnsense/scripts/unbound/blocklists/qfeeds_bl.py new file mode 100755 index 000000000..ff3413a85 --- /dev/null +++ b/security/q-feeds-connector/src/opnsense/scripts/unbound/blocklists/qfeeds_bl.py @@ -0,0 +1,55 @@ +#!/usr/local/bin/python3 + +""" + Copyright (c) 2025 Deciso B.V. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +""" + +import os +from . import BaseBlocklistHandler + +class DefaultBlocklistHandler(BaseBlocklistHandler): + def __init__(self): + super().__init__('/usr/local/etc/unbound/qfeeds-blocklists.conf') + self.priority = 100 + + def get_config(self): + cfg = {'qfeeds_filenames': []} + if self.cnf and self.cnf.has_section('settings'): + if self.cnf.has_option('settings', 'filenames'): + cfg['qfeeds_filenames'] = self.cnf.get('settings', 'filenames').split(',') + return cfg + + def get_blocklist(self): + result = {} + for filename in self.get_config()['qfeeds_filenames']: + bl_shortcode = "qf_%s" % os.path.splitext(os.path.basename(filename).strip())[0] + if os.path.exists(filename): + with open(filename, 'r') as f_in: + for line in f_in: + result[line.strip()] = {'bl': bl_shortcode, 'wildcard': False} + return result + + def get_passlist_patterns(self): + return [] diff --git a/security/q-feeds-connector/src/opnsense/service/conf/actions.d/actions_qfeeds.conf b/security/q-feeds-connector/src/opnsense/service/conf/actions.d/actions_qfeeds.conf index b693285e2..e2fba096e 100644 --- a/security/q-feeds-connector/src/opnsense/service/conf/actions.d/actions_qfeeds.conf +++ b/security/q-feeds-connector/src/opnsense/service/conf/actions.d/actions_qfeeds.conf @@ -1,5 +1,5 @@ [reconfigure] -command:/usr/local/opnsense/scripts/qfeeds/qfeedsctl.py fetch_index fetch firewall_load && echo 'EXIT OK' +command:/usr/local/opnsense/scripts/qfeeds/qfeedsctl.py fetch_index fetch firewall_load unbound_load && echo 'EXIT OK' parameters: type:script_output message:reconfigure QFeeds diff --git a/security/q-feeds-connector/src/opnsense/service/templates/OPNsense/QFeeds/+TARGETS b/security/q-feeds-connector/src/opnsense/service/templates/OPNsense/QFeeds/+TARGETS index da2b51f08..219dc39f8 100644 --- a/security/q-feeds-connector/src/opnsense/service/templates/OPNsense/QFeeds/+TARGETS +++ b/security/q-feeds-connector/src/opnsense/service/templates/OPNsense/QFeeds/+TARGETS @@ -1 +1,2 @@ qfeeds.conf:/usr/local/etc/qfeeds.conf +qfeeds-blocklists.conf:/usr/local/etc/unbound/qfeeds-blocklists.conf diff --git a/security/q-feeds-connector/src/opnsense/service/templates/OPNsense/QFeeds/qfeeds-blocklists.conf b/security/q-feeds-connector/src/opnsense/service/templates/OPNsense/QFeeds/qfeeds-blocklists.conf new file mode 100644 index 000000000..1f985bf4b --- /dev/null +++ b/security/q-feeds-connector/src/opnsense/service/templates/OPNsense/QFeeds/qfeeds-blocklists.conf @@ -0,0 +1,5 @@ +{% if not helpers.empty('OPNsense.QFeedsConnector.general.apikey') and + not helpers.empty('OPNsense.QFeedsConnector.general.enable_unbound_bl') %} +[settings] +filenames=/var/db/qfeeds-tables/malware_domains.txt +{% endif %} \ No newline at end of file