diff --git a/LICENSE b/LICENSE index f8e248f15..5cf7f7c4d 100644 --- a/LICENSE +++ b/LICENSE @@ -19,8 +19,8 @@ Copyright (c) 2008-2010 Ermal Luçi Copyright (c) 2016-2019 EURO-LOG AG Copyright (c) 2017-2020 Fabian Franz Copyright (c) 2019 Felix Matouschek -Copyright (c) 2014-2022 Franco Fichtner -Copyright (c) 2016-2022 Frank Wall +Copyright (c) 2014-2023 Franco Fichtner +Copyright (c) 2016-2023 Frank Wall Copyright (c) 2021 Github-jjw Copyright (c) 2016 IT-assistans Sverige AB Copyright (c) 2021-2023 Jan Winkler @@ -50,6 +50,7 @@ Copyright (c) 2008 Shrew Soft Inc. Copyright (c) 2017-2019 Smart-Soft Copyright (c) 2013 Stanley P. Miller \ stan-qaz Copyright (c) 2020 Starkstromkonsument +Copyright (c) 2023 Thomas Cekal Copyright (c) 2020 Tobias Boehnert Copyright (c) 2022 Wouter Deurholt Copyright (c) 2010 Yehuda Katz diff --git a/Makefile b/Makefile index d01440b96..865656059 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2020 Franco Fichtner +# Copyright (c) 2015-2023 Franco Fichtner # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Mk/plugins.mk b/Mk/plugins.mk index ca9a51fc6..16b71c55e 100644 --- a/Mk/plugins.mk +++ b/Mk/plugins.mk @@ -132,6 +132,9 @@ manifest: check done @echo "}" .endif + @if [ -f ${WRKSRC}/usr/local/opnsense/version/${PLUGIN_NAME} ]; then \ + echo "annotations $$(cat ${WRKSRC}/usr/local/opnsense/version/${PLUGIN_NAME})"; \ + fi scripts: check scripts-pre scripts-auto scripts-manual scripts-post @@ -256,14 +259,14 @@ package: check .for DEP in ${PLUGIN_DEPENDS} @if ! ${PKG} info ${DEP} > /dev/null; then ${PKG} install -yA ${DEP}; fi .endfor - @echo -n ">>> Generating metadata for ${PLUGIN_PKGNAME}-${PLUGIN_PKGVERSION}..." - @${MAKE} DESTDIR=${WRKSRC} metadata - @echo " done" @echo -n ">>> Staging files for ${PLUGIN_PKGNAME}-${PLUGIN_PKGVERSION}..." @${MAKE} DESTDIR=${WRKSRC} install @echo " done" @echo ">>> Generated version info for ${PLUGIN_PKGNAME}-${PLUGIN_PKGVERSION}:" @cat ${WRKSRC}/usr/local/opnsense/version/${PLUGIN_NAME} + @echo -n ">>> Generating metadata for ${PLUGIN_PKGNAME}-${PLUGIN_PKGVERSION}..." + @${MAKE} DESTDIR=${WRKSRC} metadata + @echo " done" @echo ">>> Packaging files for ${PLUGIN_PKGNAME}-${PLUGIN_PKGVERSION}:" @${PKG} create -v -m ${WRKSRC} -r ${WRKSRC} \ -p ${WRKSRC}/plist -o ${PKGDIR} diff --git a/dns/bind/Makefile b/dns/bind/Makefile index 77b4de062..5a305f72d 100644 --- a/dns/bind/Makefile +++ b/dns/bind/Makefile @@ -1,6 +1,6 @@ PLUGIN_NAME= bind PLUGIN_VERSION= 1.26 -PLUGIN_REVISION= 2 +PLUGIN_REVISION= 4 PLUGIN_COMMENT= BIND domain name service PLUGIN_DEPENDS= bind918 PLUGIN_MAINTAINER= m.muenz@gmail.com diff --git a/dns/bind/pkg-descr b/dns/bind/pkg-descr index 31cc0ebc0..e8edd8be2 100644 --- a/dns/bind/pkg-descr +++ b/dns/bind/pkg-descr @@ -14,6 +14,8 @@ Plugin Changelog * Allow multiple ACLs to be selected for Transfers/Queries (contributed by Robbert Rijkse) * Rename Master/Slave to Primary/Secondary (contributed by Robbert Rijkse) +* Add necessary hooks to allow the plugin to be used as a standalone core DNS server +* Changed default listening addresses to 0.0.0.0/:: for new users 1.25 diff --git a/dns/bind/src/etc/inc/plugins.inc.d/bind.inc b/dns/bind/src/etc/inc/plugins.inc.d/bind.inc index 12ef025c5..627e10808 100644 --- a/dns/bind/src/etc/inc/plugins.inc.d/bind.inc +++ b/dns/bind/src/etc/inc/plugins.inc.d/bind.inc @@ -2,6 +2,7 @@ /* * Copyright (C) 2018 Michael Muenz + * Copyright (C) 2023 Franco Fichtner * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,9 +50,22 @@ function bind_services() $model = new \OPNsense\Bind\General(); + /* DNS service is eligable for core use when both 0.0.0.0 and :: are set */ + $any4 = false; + $any6 = false; + + foreach (explode(',', (string)$model->listenv4) as $addr) { + $any4 |= $addr === '0.0.0.0'; + } + + foreach (explode(',', (string)$model->listenv6) as $addr) { + $any6 |= $addr === '::'; + } + $services[] = [ + /* the port may still be something other than 53, but it's safe to register a conflict for it */ + 'dns_ports' => ($any4 && $any6 ? [(string)$model->port] : []), 'description' => gettext('BIND Daemon'), - 'ports' => [(string)$model->port], 'configd' => [ 'restart' => ['bind restart'], 'start' => ['bind start'], diff --git a/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/General.xml b/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/General.xml index d7232ca2c..35e193df3 100644 --- a/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/General.xml +++ b/dns/bind/src/opnsense/mvc/app/models/OPNsense/Bind/General.xml @@ -16,13 +16,13 @@ Y - 127.0.0.1 + 0.0.0.0 , Y Y - ::1 + :: , Y Y @@ -149,7 +149,7 @@ Choose a value between 1 and 1000. - 127.0.0.1,::1 + 0.0.0.0,:: , Y Y diff --git a/dns/bind/src/opnsense/service/templates/OPNsense/Bind/named.conf b/dns/bind/src/opnsense/service/templates/OPNsense/Bind/named.conf index ba12c2c00..b1364adf2 100644 --- a/dns/bind/src/opnsense/service/templates/OPNsense/Bind/named.conf +++ b/dns/bind/src/opnsense/service/templates/OPNsense/Bind/named.conf @@ -68,7 +68,7 @@ options { {% if helpers.exists('OPNsense.bind.general.allowquery') and OPNsense.bind.general.allowquery != '' %} allow-query { {% for acl in helpers.toList('OPNsense.bind.general.allowquery') %} -{% set query_acl = helpers.getUUID(list) %} +{% set query_acl = helpers.getUUID(acl) %} {{ query_acl.name }}; {% endfor %} }; diff --git a/dns/ddclient/Makefile b/dns/ddclient/Makefile index 409129f4f..3eee7b353 100644 --- a/dns/ddclient/Makefile +++ b/dns/ddclient/Makefile @@ -1,6 +1,5 @@ PLUGIN_NAME= ddclient -PLUGIN_VERSION= 1.11 -PLUGIN_REVISION= 1 +PLUGIN_VERSION= 1.13 PLUGIN_DEPENDS= ddclient-devel PLUGIN_COMMENT= Dynamic DNS client PLUGIN_MAINTAINER= ad@opnsense.org diff --git a/dns/ddclient/pkg-descr b/dns/ddclient/pkg-descr index d26ea9ba9..586f76bdb 100644 --- a/dns/ddclient/pkg-descr +++ b/dns/ddclient/pkg-descr @@ -6,6 +6,18 @@ WWW: https://github.com/ddclient/ddclient Plugin Changelog ================ +1.13 + +* Fix not returning IP address as a string in Python backend (contributed by Sean Kelly) +* Fix PID file handling for Python backend + +1.12 + +* Add cloudflare implementation for Python backend (contributed by Thomas Cekal) +* Allow custom target hostname for dyndns2 protocol in Python backend +* Adjust for missing ipv6= option including upstream patches for use=/usev4=/usev6= +* Require a selected interface through validation when interface check method is used + 1.11 * Add Python backend support for custom ddclient-like implementation using the same input diff --git a/dns/ddclient/src/etc/inc/plugins.inc.d/ddclient.inc b/dns/ddclient/src/etc/inc/plugins.inc.d/ddclient.inc index b5d095910..8e087a38e 100644 --- a/dns/ddclient/src/etc/inc/plugins.inc.d/ddclient.inc +++ b/dns/ddclient/src/etc/inc/plugins.inc.d/ddclient.inc @@ -30,39 +30,46 @@ function ddclient_services() { $services = []; + $mdl = new \OPNsense\DynDNS\DynDNS(); if ($mdl->general->enabled == '1') { - $services[] = [ + $service = [ 'description' => gettext('ddclient'), 'configd' => [ - 'restart' => array('ddclient restart'), - 'start' => array('ddclient start'), - 'stop' => array('ddclient stop'), + 'restart' => ['ddclient restart'], + 'start' => ['ddclient start'], + 'stop' => ['ddclient stop'], ], 'name' => 'ddclient', - 'pidfile' => '/var/run/ddclient.pid', ]; + $service['pidfile'] = (string)$mdl->general->backend != 'opnsense' ? '/var/run/ddclient.pid' : '/var/run/ddclient_opn.pid'; + $services[] = $service; } + return $services; } function ddclient_xmlrpc_sync() { - $result = array(); - $result[] = array( + $result = []; + + $result[] = [ 'description' => gettext('ddclient'), 'section' => 'OPNsense.DynDNS', - 'id' => 'ddclient', 'services' => ['ddclient'], - ); + 'id' => 'ddclient', + ]; + return $result; } function ddclient_syslog() { - $logfacilities = array(); - $logfacilities['ddclient'] = array( - 'facility' => ['ddclient'] - ); + $logfacilities = []; + + $logfacilities['ddclient'] = [ + 'facility' => ['ddclient'], + ]; + return $logfacilities; } diff --git a/dns/ddclient/src/opnsense/mvc/app/controllers/OPNsense/DynDNS/forms/dialogAccount.xml b/dns/ddclient/src/opnsense/mvc/app/controllers/OPNsense/DynDNS/forms/dialogAccount.xml index f1c6c6e8a..c0aac07e9 100644 --- a/dns/ddclient/src/opnsense/mvc/app/controllers/OPNsense/DynDNS/forms/dialogAccount.xml +++ b/dns/ddclient/src/opnsense/mvc/app/controllers/OPNsense/DynDNS/forms/dialogAccount.xml @@ -5,6 +5,11 @@ checkbox Enable this virtual server + + account.description + + text + account.service @@ -71,6 +76,11 @@ dropdown How to determine the address to use for this host + + account.interface + + dropdown + account.checkip_timeout @@ -84,14 +94,4 @@ Force update using HTTPS, please note setting this option will enforce https updates on all accounts as ddclient only supports SSL=yes on a global level (the check ip service may still use HTTP on other services) - - account.interface - - dropdown - - - account.description - - text - diff --git a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml index 92ad3b22f..a04ca3794 100644 --- a/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml +++ b/dns/ddclient/src/opnsense/mvc/app/models/OPNsense/DynDNS/DynDNS.xml @@ -153,6 +153,11 @@ zoneedit Interface + + + interface.check001 + + 10 @@ -167,6 +172,14 @@ N N + + + An interface is required for the selected check method + SetIfConstraint + checkip + if + + N diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/azure.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/azure.py index ff28fdef9..11fd3c637 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/azure.py +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/azure.py @@ -85,10 +85,7 @@ class Azure(BaseAccount): return Azure._services def match(account): - if account.get('service') in Azure._services: - return True - else: - return False + return account.get('service') in Azure._services def execute(self): """ Azure DNS update, uses an oauth2 sequence to login, the following requests are being performed: diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/cloudflare.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/cloudflare.py new file mode 100755 index 000000000..efd5f5612 --- /dev/null +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/cloudflare.py @@ -0,0 +1,175 @@ +""" + Copyright (c) 2023 Thomas Cekal + Copyright (c) 2023 Ad Schellevis + 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 json +import syslog +import requests +from . import BaseAccount + + +class Cloudflare(BaseAccount): + _priority = 65535 + + _services = { + 'cloudflare': 'api.cloudflare.com' + } + + def __init__(self, account: dict): + super().__init__(account) + + @staticmethod + def known_services(): + return Cloudflare._services.keys() + + def match(account): + return account.get('service') in Cloudflare._services + + def execute(self): + if super().execute(): + # IPv4/IPv6 + recordType = None + if str(self.current_address).find(':') > 1: + #IPv6 + recordType = "AAAA" + else: + #IPv4 + recordType = "A" + + # get ZoneID + url = "https://%s/client/v4/zones" % self._services[self.settings.get('service')] + req_opts = { + 'url': url, + 'params': { + 'name': self.settings.get('zone') + }, + 'headers': { + 'User-Agent': 'OPNsense-dyndns', + 'X-Auth-Email': self.settings.get('username'), + 'X-Auth-Key': self.settings.get('password') + } + } + response = requests.get(**req_opts) + try: + payload = response.json() + except requests.exceptions.JSONDecodeError: + payload = {} + if 'success' not in payload: + syslog.syslog( + syslog.LOG_ERR, + "Account %s error parsing JSON response [ZoneID] %s" % (self.description, response.text) + ) + return + if not payload.get('success', False): + syslog.syslog( + syslog.LOG_ERR, + "Account %s error receiving ZoneID [%s]" % (self.description, json.dumps(payload.get('errors', {}))) + ) + return + + zone_id = payload['result'][0]['id'] + if self.is_verbose: + syslog.syslog( + syslog.LOG_NOTICE, + "Account %s ZoneID for %s %s" % (self.description, self.settings.get('zone'), zone_id) + ) + + # Get record ID + req_opts = { + 'url': f"{req_opts['url']}/{zone_id}/dns_records", + 'params': { + 'name': self.settings.get('hostnames'), + 'type': recordType + }, + 'headers': req_opts['headers'] + } + response = requests.get(**req_opts) + try: + payload = response.json() + except requests.exceptions.JSONDecodeError: + payload = {} + if 'success' not in payload: + syslog.syslog( + syslog.LOG_ERR, + "Account %s error parsing JSON response [RecordID] %s" % (self.description, response.text) + ) + return + if not payload.get('success', False): + syslog.syslog( + syslog.LOG_ERR, + "Account %s error receiving RecordID [%s]" % ( + self.description, json.dumps(payload.get('errors', {})) + ) + ) + return + + record_id = payload['result'][0]['id'] + if self.is_verbose: + syslog.syslog( + syslog.LOG_NOTICE, + "Account %s RecordID for %s %s" % (self.description, self.settings.get('hostnames'), record_id) + ) + + # Send IP address update + req_opts = { + 'url': f"{req_opts['url']}/{record_id}", + 'json': { + 'type': recordType, + 'name': self.settings.get('hostnames'), + 'content': str(self.current_address) + }, + 'headers': req_opts['headers'] + } + response = requests.put(**req_opts) + try: + payload = response.json() + except requests.exceptions.JSONDecodeError: + payload = {} + if 'success' not in payload: + syslog.syslog( + syslog.LOG_ERR, + "Account %s error parsing JSON response [UpdateIP] %s" % (self.description, response.text) + ) + return + if payload.get('success', False): + syslog.syslog( + syslog.LOG_NOTICE, + "Account %s set new ip %s [%s]" % ( + self.description, + self.current_address, + payload.get('result', {}).get('content', '') + ) + ) + + self.update_state(address=self.current_address) + return True + else: + syslog.syslog( + syslog.LOG_ERR, + "Account %s failed to set new ip %s [%s]" % (self.description, self.current_address, response.text) + ) + + + return False diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/dyndns2.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/dyndns2.py index 2ea0649c3..e53b14c83 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/dyndns2.py +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/account/dyndns2.py @@ -53,7 +53,7 @@ class DynDNS2(BaseAccount): @staticmethod def known_services(): - return DynDNS2._services.keys() + return list(DynDNS2._services.keys()) + ['custom'] def match(account): if account.get('service') in DynDNS2._services or ( diff --git a/dns/ddclient/src/opnsense/scripts/ddclient/lib/address.py b/dns/ddclient/src/opnsense/scripts/ddclient/lib/address.py index b5071214b..16a755788 100755 --- a/dns/ddclient/src/opnsense/scripts/ddclient/lib/address.py +++ b/dns/ddclient/src/opnsense/scripts/ddclient/lib/address.py @@ -91,7 +91,7 @@ def checkip(service, proto='https', timeout='10', interface=None): try: address = ipaddress.ip_address(parts[1]) if address.is_global: - return address + return str(address) except ValueError: continue else: diff --git a/dns/ddclient/src/opnsense/service/conf/actions.d/actions_ddclient.conf b/dns/ddclient/src/opnsense/service/conf/actions.d/actions_ddclient.conf index 7d3ddf360..ce63a7ef8 100644 --- a/dns/ddclient/src/opnsense/service/conf/actions.d/actions_ddclient.conf +++ b/dns/ddclient/src/opnsense/service/conf/actions.d/actions_ddclient.conf @@ -12,10 +12,7 @@ type:script message:stopping ddclient [status] -command: - pgrep -qF /var/run/ddclient.pid 2> /dev/null && echo "ddclient is running" || - pgrep -qF /var/run/ddclient_opn.pid 2> /dev/null && echo "ddclient is running" || - echo "ddclient is not running" +command:/usr/local/sbin/pluginctl -s ddclient status type:script_output message:get ddclient status diff --git a/dns/ddclient/src/opnsense/service/templates/OPNsense/ddclient/ddclient.conf b/dns/ddclient/src/opnsense/service/templates/OPNsense/ddclient/ddclient.conf index cf79c745a..9989c794a 100644 --- a/dns/ddclient/src/opnsense/service/templates/OPNsense/ddclient/ddclient.conf +++ b/dns/ddclient/src/opnsense/service/templates/OPNsense/ddclient/ddclient.conf @@ -4,9 +4,7 @@ pid=/var/run/ddclient.pid # record PID in file. {% if not helpers.empty('OPNsense.DynDNS.general.verbose') %} verbose=yes {% endif %} -{% if not helpers.empty('OPNsense.DynDNS.general.allowipv6') %} -ipv6=yes -{% endif %} + {% set accounts = [] %} {% set force_ssl = [] %} {% if helpers.exists('OPNsense.DynDNS.accounts.account') and OPNsense.DynDNS.general.backend|default('ddclient') == 'ddclient' %} @@ -23,10 +21,12 @@ ipv6=yes ssl=yes {% endif %} - {% for account in accounts %} -{% if account.checkip == 'if' %} -use=if, if={{physical_interface(account.interface)}}, \ +{% if account.checkip == 'if' %} +{% if not helpers.empty('OPNsense.DynDNS.general.allowipv6') %} +usev6=ifv6, \ +{% endif %} +usev4=ifv4, if={{physical_interface(account.interface)}}, \ {% elif account.checkip.startswith('web_') %} {% if account.interface %} use=cmd, cmd="/usr/local/opnsense/scripts/ddclient/checkip -i {{physical_interface(account.interface)}} -t {{account.force_ssl}} -s {{account.checkip[4:]}} --timeout {{account.checkip_timeout|default('10')}}", diff --git a/dns/dnscrypt-proxy/Makefile b/dns/dnscrypt-proxy/Makefile index ef81bb13e..39d70dd46 100644 --- a/dns/dnscrypt-proxy/Makefile +++ b/dns/dnscrypt-proxy/Makefile @@ -1,6 +1,5 @@ PLUGIN_NAME= dnscrypt-proxy -PLUGIN_VERSION= 1.12 -PLUGIN_REVISION= 1 +PLUGIN_VERSION= 1.13 PLUGIN_COMMENT= Flexible DNS proxy supporting DNSCrypt and DoH PLUGIN_DEPENDS= dnscrypt-proxy2 PLUGIN_MAINTAINER= m.muenz@gmail.com diff --git a/dns/dnscrypt-proxy/pkg-descr b/dns/dnscrypt-proxy/pkg-descr index 0be7fb48e..76d19e885 100644 --- a/dns/dnscrypt-proxy/pkg-descr +++ b/dns/dnscrypt-proxy/pkg-descr @@ -5,6 +5,11 @@ such as DNSCrypt v2 and DNS-over-HTTPS. Plugin Changelog ================ +1.13 + +* Add necessary hooks to allow the plugin to be used as a standalone core DNS server +* Changed default listening addresses to 0.0.0.0/:: for new users + 1.12 * Support specifying relays for anonymous DNS diff --git a/dns/dnscrypt-proxy/src/etc/inc/plugins.inc.d/dnscryptproxy.inc b/dns/dnscrypt-proxy/src/etc/inc/plugins.inc.d/dnscryptproxy.inc index 3f3588874..aeb8ef1ff 100644 --- a/dns/dnscrypt-proxy/src/etc/inc/plugins.inc.d/dnscryptproxy.inc +++ b/dns/dnscrypt-proxy/src/etc/inc/plugins.inc.d/dnscryptproxy.inc @@ -1,30 +1,31 @@ - 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. -*/ + * Copyright (C) 2018 Michael Muenz + * Copyright (C) 2023 Franco Fichtner + * 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. + */ function dnscryptproxy_enabled() { @@ -32,24 +33,62 @@ function dnscryptproxy_enabled() return (string)$model->enabled == '1'; } +function dnscryptproxy_configure() +{ + return [ + 'dns' => ['dnscryptproxy_configure_do'], + ]; +} + function dnscryptproxy_services() { - $services = array(); + $services = []; if (!dnscryptproxy_enabled()) { return $services; } - $services[] = array( + $model = new \OPNsense\Dnscryptproxy\General(); + $ports = []; + + /* + * DNS service is eligable for core use when both 0.0.0.0 and :: are set. + * In order to provide dual stack ports we need to intersect the resulting + * ports for each address family. + */ + $localhost4 = []; + $localhost6 = []; + + foreach (explode(',', (string)$model->listen_addresses) as $addrport) { + if (preg_match('/^0\.0\.0\.0:([\d]+)$/', $addrport, $matches)) { + $localhost4[$matches[1]] = 1; + } elseif (preg_match('/^\[::\]:([\d]+)$/', $addrport, $matches)) { + $localhost6[$matches[1]] = 1; + } + } + + $services[] = [ + /* the port may still be something other than 53, but it's safe to register a conflict for it */ + 'dns_ports' => array_keys(array_intersect_key($localhost4, $localhost6)), 'description' => gettext('DNSCrypt-Proxy'), - 'configd' => array( - 'restart' => array('dnscryptproxy restart'), - 'start' => array('dnscryptproxy start'), - 'stop' => array('dnscryptproxy stop'), - ), + 'configd' => [ + 'restart' => ['dnscryptproxy restart'], + 'start' => ['dnscryptproxy start'], + 'stop' => ['dnscryptproxy stop'], + ], + 'pid' => '/var/run/dnscrypt-proxy.pid', 'name' => 'dnscrypt-proxy', - 'pid' => '/var/run/dnscrypt-proxy.pid' - ); + ]; return $services; } + +function dnscryptproxy_configure_do($verbose) +{ + service_log('Starting DNSCrypt-Proxy...', $verbose); + + configd_run('template reload OPNsense/Dnscryptproxy'); + configd_run('dnscryptproxy restart'); + + service_log("done.\n", $verbose); +} diff --git a/dns/dnscrypt-proxy/src/opnsense/mvc/app/controllers/OPNsense/Dnscryptproxy/forms/general.xml b/dns/dnscrypt-proxy/src/opnsense/mvc/app/controllers/OPNsense/Dnscryptproxy/forms/general.xml index 9243e2372..7f5d0e853 100644 --- a/dns/dnscrypt-proxy/src/opnsense/mvc/app/controllers/OPNsense/Dnscryptproxy/forms/general.xml +++ b/dns/dnscrypt-proxy/src/opnsense/mvc/app/controllers/OPNsense/Dnscryptproxy/forms/general.xml @@ -11,7 +11,7 @@ select_multiple true - Set the IP address and port combinations this service should listen on, e.g 127.0.0.1:5353 and/or [::1]:5353 + Set the IP address/port combinations this service should listen on. general.allowprivileged diff --git a/dns/dnscrypt-proxy/src/opnsense/mvc/app/models/OPNsense/Dnscryptproxy/General.xml b/dns/dnscrypt-proxy/src/opnsense/mvc/app/models/OPNsense/Dnscryptproxy/General.xml index 3d47b93fd..4dd48bdfd 100644 --- a/dns/dnscrypt-proxy/src/opnsense/mvc/app/models/OPNsense/Dnscryptproxy/General.xml +++ b/dns/dnscrypt-proxy/src/opnsense/mvc/app/models/OPNsense/Dnscryptproxy/General.xml @@ -1,15 +1,15 @@ //OPNsense/dnscryptproxy/general dnscrypt-proxy configuration - 0.1.1 + 0.1.2 0 Y - 127.0.0.1:5353,[::1]:5353 - N + 0.0.0.0:5353,[::]:5353 + Y 0 diff --git a/net/relayd/src/opnsense/mvc/tests/app/compound/OPNsense/Relayd/RelaydTest.php b/net/relayd/src/opnsense/mvc/tests/app/compound/OPNsense/Relayd/RelaydTest.php deleted file mode 100644 index 8df0b737a..000000000 --- a/net/relayd/src/opnsense/mvc/tests/app/compound/OPNsense/Relayd/RelaydTest.php +++ /dev/null @@ -1,469 +0,0 @@ -mdlRelayd->$nodeType->getNodes(); - foreach ($nodes as $nodeUuid => $node) { - self::$setRelayd->mdlRelayd->$nodeType->del($nodeUuid); - } - } - - /** - * test getAction - */ - public function testGet() - { - $this->assertInstanceOf('\OPNsense\Relayd\Api\SettingsController', self::$setRelayd); - $this->expectException(\Exception::class); - $response = self::$setRelayd->getAction('wrong_node_type'); - $testConfig = []; - $response = self::$setRelayd->getAction('general'); - $testConfig['general'] = $response['relayd']['general']; - - $this->assertEquals($response['status'], 'ok'); - $this->assertArrayHasKey('enabled', $response['relayd']['general']); - - return $testConfig; - } - - /** - * test searchAction - * @depends testGet - */ - public function testSearch($testConfig) - { - $_SERVER['REQUEST_METHOD'] = 'POST'; - $_POST = array('current' => '1', 'rowCount' => '7'); - - foreach ($this->nodeTypes as $nodeType) { - $response = self::$setRelayd->searchAction($nodeType); - $this->assertArrayHasKey('total', $response); - $testConfig[$nodeType] = $response['rows']; - } - - return $testConfig; - } - - /** - * test delAction - * not really a test if the config is empty, but we will delete something later - * @depends testSearch - */ - public function testReset($testConfig) - { - $_SERVER['REQUEST_METHOD'] = 'POST'; - foreach (array_reverse($this->nodeTypes) as $nodeType) { - foreach ($testConfig[$nodeType] as $node) { - $response = self::$setRelayd->delAction($nodeType, $node['uuid']); - $this->assertEquals($response['status'], 'ok'); - } - } - // need an assertion here to succeed this test on empty config - $this->assertTrue(true); - } - - /** - * test setAction general - * @depends testReset - */ - public function testSetGeneral() - { - $_SERVER['REQUEST_METHOD'] = 'POST'; - - // interval too small - $_POST = array('relayd' => ['general' => ['interval' => '0']]); - $response = self::$setRelayd->setAction('general'); - $this->assertCount(1, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.general.interval']); - - // set correct interval and incorrect timeout (s. testServiceController) - $_POST = array('relayd' => ['general' => ['interval' => '10', 'timeout' => 86400, 'enabled' => '0']]); - $response = self::$setRelayd->setAction('general'); - $this->assertEquals($response['status'], 'ok'); - } - - /** - * test dirtyAction - * @depends testSetGeneral - */ - public function testDirtyAction() - { - $this->assertInstanceOf('\OPNsense\Relayd\Api\SettingsController', self::$setRelayd); - $response = self::$setRelayd->dirtyAction(); - $this->assertEquals($response['status'], 'ok'); - $this->assertEquals($response['relayd']['dirty'], true); - } - - /** - * test setAction for hosts - * @depends testReset - */ - public function testSetHost() - { - $_SERVER['REQUEST_METHOD'] = 'POST'; - - // empty host name - $_POST = array('relayd' => ['host' => ['address' => '127.0.0.1']]); - $response = self::$setRelayd->setAction('host'); - $this->assertCount(1, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.host.name']); - $this->cleanupNodes('host'); - - // check mask - $_POST = array('relayd' => ['host' => ['name' => 'test$Host', 'address' => '127.0.0.$']]); - $response = self::$setRelayd->setAction('host'); - $this->assertCount(2, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.host.name']); - $this->assertNotEmpty($response['validations']['relayd.host.address']); - $this->cleanupNodes('host'); - - // create host for ServiceControllerTest - $_POST = array('relayd' => ['host' => ['name' => 'testHost', 'address' => '127.0.0.1']]); - $response = self::$setRelayd->setAction('host'); - $this->assertEquals($response['status'], 'ok'); - } - - /** - * test setAction for tables - * @depends testSetHost - */ - public function testSetTable() - { - $_SERVER['REQUEST_METHOD'] = 'POST'; - - // check mask and missing host - $_POST = array('relayd' => ['table' => ['name' => 'test$Table', 'hosts' => 'aaa-111-bbb-222']]); - $response = self::$setRelayd->setAction('table'); - $this->assertCount(2, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.table.name']); - $this->assertNotEmpty($response['validations']['relayd.table.hosts']); - $this->cleanupNodes('table'); - - // create table for ServiceControllerTest - $_POST = array('current' => '1', 'rowCount' => '7', 'searchPhrase' => 'testHost'); - $response = self::$setRelayd->searchAction('host'); - $this->assertArrayHasKey('total', $response); - $_POST = array('relayd' => [ - 'table' => ['name' => 'testTable', 'enabled' => 1, 'hosts' => $response['rows'][0]['uuid']] - ]); - $response = self::$setRelayd->setAction('table'); - } - - /** - * test setAction for tablechecks - * @depends testSearch - * @depends testReset - */ - public function testSetTableCheck() - { - $_SERVER['REQUEST_METHOD'] = 'POST'; - - // wrong option - $_POST = array('relayd' => ['tablecheck' => ['name' => 'test$Check', 'type' => 'ABCXYZ']]); - $response = self::$setRelayd->setAction('tablecheck'); - $this->assertCount(2, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.tablecheck.name']); - $this->assertNotEmpty($response['validations']['relayd.tablecheck.type']); - $this->cleanupNodes('tablecheck'); - - // type 'send' without 'expect' - $_POST = array('relayd' => ['tablecheck' => ['name' => 'testSend', 'type' => 'send']]); - $response = self::$setRelayd->setAction('tablecheck'); - $this->assertCount(1, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.tablecheck.expect']); - $this->cleanupNodes('tablecheck'); - - // type 'script' without 'path' - $_POST = array('relayd' => ['tablecheck' => ['name' => 'testScript', 'type' => 'script']]); - $response = self::$setRelayd->setAction('tablecheck'); - $this->assertCount(1, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.tablecheck.path']); - $this->cleanupNodes('tablecheck'); - - // type 'http' without 'code' and 'digest' - $_POST = array('relayd' => [ - 'tablecheck' => ['name' => 'testTableCheck', 'type' => 'http', 'path' => 'http://www.example.com'] - ]); - $response = self::$setRelayd->setAction('tablecheck'); - $this->assertCount(2, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.tablecheck.code']); - $this->assertNotEmpty($response['validations']['relayd.tablecheck.digest']); - $this->cleanupNodes('tablecheck'); - - // create tablecheck for ServiceControllerTest - $_POST = array('relayd' => [ - 'tablecheck' => [ - 'name' => 'testTableCheck', - 'type' => 'http', - 'path' => '/', - 'host' => 'localhost', - 'code' => '403', - 'ssl' => '1']]); - $response = self::$setRelayd->setAction('tablecheck'); - $this->assertEquals($response['status'], 'ok'); - } - - /** - * test setAction for protocols - * @depends testSearch - * @depends testReset - */ - public function testSetProtocol() - { - $_SERVER['REQUEST_METHOD'] = 'POST'; - - // missing 'name' wrong 'type' - $_POST = array('relayd' => ['protocol' => ['name' => 'test$Protocol', 'type' => 'ABCXYZ']]); - $response = self::$setRelayd->setAction('protocol'); - $this->assertCount(2, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.protocol.name']); - $this->assertNotEmpty($response['validations']['relayd.protocol.type']); - $this->cleanupNodes('protocol'); - - // create protocol for ServiceControllerTest - $_POST = array('relayd' => [ - 'protocol' => ['name' => 'testProtocol', 'type' => 'tcp', 'options' => 'nodelay, socket buffer 65536'] - ]); - $response = self::$setRelayd->setAction('protocol'); - $this->assertEquals($response['status'], 'ok'); - } - - /** - * test setAction for virtualservers - * @depends testSearch - * @depends testReset - */ - public function testSetVirtualServer() - { - $_SERVER['REQUEST_METHOD'] = 'POST'; - - // search table and tablecheck - $_POST = array('current' => '1', 'rowCount' => '7', 'searchPhrase' => 'testTable'); - $response = self::$setRelayd->searchAction('table'); - $this->assertArrayHasKey('total', $response); - $tableUuid = $response['rows'][0]['uuid']; - $_POST = array('current' => '1', 'rowCount' => '7', 'searchPhrase' => 'testTableCheck'); - $response = self::$setRelayd->searchAction('tablecheck'); - $this->assertArrayHasKey('total', $response); - $tableCheckUuid = $response['rows'][0]['uuid']; - $_POST = array('current' => '1', 'rowCount' => '7', 'searchPhrase' => 'testProtocol'); - $response = self::$setRelayd->searchAction('protocol'); - $this->assertArrayHasKey('total', $response); - $protocolUuid = $response['rows'][0]['uuid']; - - // check mask, misisng table, tablecheck, wrong/missing listen port/address - $_POST = array('relayd' => [ - 'virtualserver' => [ - 'name' => 'test{}VirtualServer', - 'listen_startport' => '123456', - ]]); - $response = self::$setRelayd->setAction('virtualserver'); - $this->assertCount(5, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.virtualserver.name']); - $this->assertNotEmpty($response['validations']['relayd.virtualserver.listen_address']); - $this->assertNotEmpty($response['validations']['relayd.virtualserver.listen_startport']); - $this->assertNotEmpty($response['validations']['relayd.virtualserver.transport_table']); - $this->assertNotEmpty($response['validations']['relayd.virtualserver.transport_tablecheck']); - $this->cleanupNodes('virtualserver'); - - // wrong tablemodes, missing ModelRelationField targets - $_POST = array('relayd' => [ - 'virtualserver' => [ - 'name' => 'testVirtualServer', - 'listen_address' => '127.0.0.1', - 'listen_startport' => '444', - 'transport_table' => $tableUuid, - 'transport_tablemode' => 'least-states', - 'transport_tablecheck' => $tableCheckUuid, - ]]); - $response = self::$setRelayd->setAction('virtualserver'); - $this->assertCount(1, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.virtualserver.transport_tablemode']); - $this->cleanupNodes('virtualserver'); - - // wron scheduler, missing protocol - $_POST = array('relayd' => [ - 'virtualserver' => [ - 'name' => 'testVirtualServer', - 'type' => 'redirect', - 'listen_address' => '127.0.0.1', - 'listen_startport' => '444', - 'transport_table' => $tableUuid, - 'transport_tablemode' => 'least-states', - 'transport_tablecheck' => $tableCheckUuid, - 'backuptransport_table' => $tableUuid, - 'backuptransport_tablemode' => 'random', - 'backuptransport_tablecheck' => $tableCheckUuid, - 'protocol' => 'aaa-bbb-123-456' - ]]); - $response = self::$setRelayd->setAction('virtualserver'); - $this->assertCount(2, $response['validations']); - $this->assertEquals($response['result'], 'failed'); - $this->assertNotEmpty($response['validations']['relayd.virtualserver.backuptransport_tablemode']); - $this->assertNotEmpty($response['validations']['relayd.virtualserver.protocol']); - $this->cleanupNodes('virtualserver'); - - // create virtualserver for ServiceControllerTest - $_POST = array('relayd' => [ - 'virtualserver' => [ - 'name' => 'testVirtualServer', - 'enabled' => '1', - 'listen_address' => '127.0.0.1', - 'listen_startport' => '444', - 'transport_table' => $tableUuid, - 'transport_port' => '443', - 'transport_tablecheck' => $tableCheckUuid, - 'protocol' => $protocolUuid - ]]); - $response = self::$setRelayd->setAction('virtualserver'); - $this->assertEquals($response['status'], 'ok'); - } - - /** - * ServiceControllerTest - * @depends testSetGeneral - * @depends testSetHost - * @depends testSetTable - * @depends testSetTableCheck - * @depends testSetProtocol - * @depends testSetVirtualServer - */ - public function testServiceController() - { - $svcRelayd = new \OPNsense\Relayd\Api\ServiceController(); - $_SERVER['REQUEST_METHOD'] = 'POST'; - - // stop possibly running service - $response = $svcRelayd->stopAction(); - $this->assertEquals($response['response'], "OK\n\n"); - - // generate template and test it by Relayd - $response = $svcRelayd->configtestAction(); - $this->assertEquals($response['template'], 'OK'); - $this->assertEquals( - $response['result'], - "global timeout exceeds interval\ntable timeout exceeds interval: testTable:443" - ); - $_POST = array('relayd' => ['general' => ['timeout' => '200']]); - $response = self::$setRelayd->setAction('general'); - $this->assertEquals($response['status'], 'ok'); - $response = $svcRelayd->configtestAction(); - $this->assertEquals($response['template'], 'OK'); - $this->assertEquals($response['result'], 'configuration OK'); - - // status - $response = $svcRelayd->statusAction(); - $this->assertEquals($response['status'], 'disabled'); - - // enable - $_POST = array('relayd' => ['general' => ['enabled' => '1']]); - $response = self::$setRelayd->setAction('general'); - $this->assertEquals($response['status'], 'ok'); - - // reconfigure - $response = $svcRelayd->reconfigureAction(); - $this->assertEquals($response['status'], 'ok'); - - // status - $response = $svcRelayd->statusAction(); - $this->assertEquals($response['status'], 'running'); - } - - /** - * StatusControllerTest - * @depends testServiceController - */ - public function testStatusController() - { - $statRelayd = new \OPNsense\Relayd\Api\StatusController(); - $response = $statRelayd->sumAction(); - $this->assertEquals($response['result'], 'ok'); - $this->assertEquals($response['rows'][0]['type'], 'relay'); - $this->assertEquals($response['rows'][0]['name'], 'testVirtualServer'); - $this->assertEquals($response['rows'][0]['tables'][1]['name'], 'testTable:443'); - $this->assertEquals($response['rows'][0]['tables'][1]['status'], 'active (1 hosts)'); - $this->assertEquals($response['rows'][0]['tables'][1]['hosts'][1]['name'], '127.0.0.1'); - - $response = $statRelayd->toggleAction('table', 1, 'disable'); - $this->assertEquals($response['result'], 'ok'); - $this->assertEquals($response['output'], 'command succeeded'); - } - - /** - * cleanup config - * @depends testStatusController - */ - public function testCleanup() - { - $svcRelayd = new \OPNsense\Relayd\Api\ServiceController(); - $response = $svcRelayd->stopAction(); - $this->assertEquals($response['response'], "OK\n\n"); - - foreach (array_reverse($this->nodeTypes) as $nodeType) { - $this->cleanupNodes($nodeType); - } - - $general = self::$setRelayd->mdlRelayd->getNodeByReference('general'); - $general->setNodes(array('enabled' => '0')); - - self::$setRelayd->mdlRelayd->serializeToConfig(); - Config::getInstance()->save(); - $this->assertTrue(true); - } -} diff --git a/net/udpbroadcastrelay/Makefile b/net/udpbroadcastrelay/Makefile index 9d11bafa5..850085eb9 100644 --- a/net/udpbroadcastrelay/Makefile +++ b/net/udpbroadcastrelay/Makefile @@ -1,6 +1,6 @@ PLUGIN_NAME= udpbroadcastrelay PLUGIN_VERSION= 1.0 -PLUGIN_REVISION= 2 +PLUGIN_REVISION= 3 PLUGIN_COMMENT= Control ubpbroadcastrelay processes PLUGIN_DEPENDS= udpbroadcastrelay PLUGIN_MAINTAINER= mjwasley@gmail.com diff --git a/net/udpbroadcastrelay/src/etc/inc/plugins.inc.d/udpbroadcastrelay.inc b/net/udpbroadcastrelay/src/etc/inc/plugins.inc.d/udpbroadcastrelay.inc index bb8a14f9f..aeddc27c4 100644 --- a/net/udpbroadcastrelay/src/etc/inc/plugins.inc.d/udpbroadcastrelay.inc +++ b/net/udpbroadcastrelay/src/etc/inc/plugins.inc.d/udpbroadcastrelay.inc @@ -1,29 +1,29 @@ udpbroadcastrelay->iterateItems() as $server) { - if ($server->enabled == '1') { + if ((string)$server->enabled == '1') { return true; } } @@ -48,7 +48,7 @@ function udpbroadcastrelay_firewall($fw) function udpbroadcastrelay_services() { - $services = array(); + $services = []; if (!udpbroadcastrelay_enabled()) { return $services; @@ -57,22 +57,21 @@ function udpbroadcastrelay_services() $model = new \OPNsense\UDPBroadcastRelay\UDPBroadcastRelay(); foreach ($model->udpbroadcastrelay->iterateItems() as $server) { - if ($server->enabled == '0') { + if ((string)$server->enabled == '0') { continue; } - $services[] = array( - 'description' => $server->description, - 'id' => $server->InstanceID, + $services[] = [ + 'description' => (string)$server->description, + 'id' => (string)$server->InstanceID, 'pidfile' => "/var/run/udpbroadcastrelay_{$server->InstanceID}.pid", - 'configd' => array( - 'restart' => array('udpbroadcastrelay restart ' . $server->InstanceID), - 'start' => array('udpbroadcastrelay start ' . $server->InstanceID), - 'stop' => array('udpbroadcastrelay stop ' . $server->InstanceID), - - ), + 'configd' => [ + 'restart' => ['udpbroadcastrelay restart ' . $server->InstanceID], + 'start' => ['udpbroadcastrelay start ' . $server->InstanceID], + 'stop' => ['udpbroadcastrelay stop ' . $server->InstanceID], + ], 'name' => 'udpbroadcastrelay', - ); + ]; } return $services; diff --git a/net/upnp/Makefile b/net/upnp/Makefile index 7a170f82e..73c79af48 100644 --- a/net/upnp/Makefile +++ b/net/upnp/Makefile @@ -1,6 +1,6 @@ PLUGIN_NAME= upnp PLUGIN_VERSION= 1.5 -PLUGIN_REVISION= 2 +PLUGIN_REVISION= 3 PLUGIN_DEPENDS= miniupnpd-devel PLUGIN_COMMENT= Universal Plug and Play Service PLUGIN_MAINTAINER= franco@opnsense.org diff --git a/net/upnp/pkg-descr b/net/upnp/pkg-descr index 091bcabff..fc73e6b00 100644 --- a/net/upnp/pkg-descr +++ b/net/upnp/pkg-descr @@ -9,6 +9,7 @@ Plugin Changelog 1.5 -* enable STUN and allow LAN subnet override (contributed by Tawmu) -* add missing firewall anchors (contributed by Tawmu) -* switch to miniupnpd 2.3.1 +* Enable STUN and allow LAN subnet override (contributed by Tawmu) +* Add missing firewall anchors (contributed by Tawmu) +* Allow subnet mask 0 in rules (contributed by Reiko Asakura) +* Switch to miniupnpd 2.3.1 diff --git a/net/upnp/src/www/services_upnp.php b/net/upnp/src/www/services_upnp.php index bbd1436e9..3a73b5700 100644 --- a/net/upnp/src/www/services_upnp.php +++ b/net/upnp/src/www/services_upnp.php @@ -39,7 +39,7 @@ function miniupnpd_validate_ip($ip) $ip_array = []; $ip_array = explode('/', $ip); if (count($ip_array) == 2) { - if ($ip_array[1] < 1 || $ip_array[1] > 32) { + if ($ip_array[1] < 0 || $ip_array[1] > 32) { return false; } } elseif (count($ip_array) != 1) { diff --git a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/SftpClient.php b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/SftpClient.php index 064b13403..1804596af 100644 --- a/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/SftpClient.php +++ b/security/acme-client/src/opnsense/mvc/app/library/OPNsense/AcmeClient/SftpClient.php @@ -199,17 +199,28 @@ class SftpClient $this->processAvailableInput(); $this->process->put("ls -la"); - $regex = '/^([bcdlsp\-][rwx\-]{9}[+@]?)\s+[0-9]+\s+([^\s]+)\s+([^\s]+)\s+([0-9]+)\s+(\w+\s+[0-9]+\s+[0-9:]+)\s+(.+)$/'; + $regex = '/^' + . '(?P[?bcCdDlMnpPs\-])' + . '(?P([rw\-]{2}[sStTx\-]){3})' + . '(?P[^\s])?' . '\s+' + . '(?P[^\s]+)' . '\s+' + . '(?P[^\s]+)' . '\s+' + . '(?P[^\s]+)' . '\s+' + . '(?P[^\s]+)' . '\s+' + . '(?P\w+\s+[0-9]+\s+[0-9:]+)' . '\s+' + . '(?P.+)' . '\s*' + . '$/'; + $this->processAvailableInput(self::COMMAND_REPLY_TIMEOUT, 2, function ($line) use (&$files, $regex) { if (preg_match($regex, $line, $matches)) { - $filename = trim(stripcslashes($matches[6])); // decodes octal UTF-8 sequences + $filename = trim(stripcslashes($matches["filename"])); // decodes octal UTF-8 sequences $files[$filename] = [ - "type" => $matches[1][0], - "permissions" => $matches[1], - "owner" => $matches[2], - "group" => $matches[3], - "size" => intval($matches[4]), - "mtime" => strtotime($matches[5]) + "type" => $matches["type"], + "permissions" => $matches["permissions"], + "owner" => stripcslashes($matches["owner"]), + "group" => stripcslashes($matches["group"]), + "size" => intval($matches["size"]), + "mtime" => strtotime($matches["mtime"]) ]; return true; } diff --git a/security/crowdsec/Makefile b/security/crowdsec/Makefile index c77c5f821..a731b72a7 100644 --- a/security/crowdsec/Makefile +++ b/security/crowdsec/Makefile @@ -1,5 +1,5 @@ PLUGIN_NAME= crowdsec -PLUGIN_VERSION= 1.0.2 +PLUGIN_VERSION= 1.0.4 PLUGIN_DEPENDS= crowdsec PLUGIN_COMMENT= Lightweight and collaborative security engine PLUGIN_MAINTAINER= marco@crowdsec.net diff --git a/security/crowdsec/pkg-descr b/security/crowdsec/pkg-descr index 846f7e8e8..aba38f1c2 100644 --- a/security/crowdsec/pkg-descr +++ b/security/crowdsec/pkg-descr @@ -8,6 +8,17 @@ WWW: https://crowdsec.net/ Plugin Changelog ================ +1.0.4 + +* Add force_inotify option to aquire logs when /var/log is in RAM, otherwise + a restart of the service is required after a reboot. + +1.0.3 + +* acquire filter logs for the firewallservices/pf collection (port scans). + If you already added it manually, you can remove it now to avoid counting + the events twice. + 1.0.2 * updated cron job (reload only when there are updates), added small random delay diff --git a/security/crowdsec/src/etc/crowdsec/acquis.d/opnsense.yaml b/security/crowdsec/src/etc/crowdsec/acquis.d/opnsense.yaml index ab73fcd90..7867ccdae 100644 --- a/security/crowdsec/src/etc/crowdsec/acquis.d/opnsense.yaml +++ b/security/crowdsec/src/etc/crowdsec/acquis.d/opnsense.yaml @@ -9,10 +9,20 @@ filenames: # DO NOT EDIT - to add new datasources (log locations), # create new files in /usr/local/etc/crowdsec/acquis.d/ - # + # collection: crowdsecurity/sshd - /var/log/audit/latest.log # collection: crowdsecurity/opnsense-gui (web admin) - /var/log/lighttpd/latest.log + # collection: firewallservices/pf + - /var/log/filter/latest.log + +# When OPNsense is configured with /var/log in a RAM disk, +# the log directories are created after crowdsec is run. +# We force crowdsec to watch over directory creation as well +# as file creation. FreeBSD has kqueue instead of inotify +# but the option works with both. +force_inotify: true + labels: type: syslog diff --git a/security/crowdsec/src/opnsense/mvc/app/models/OPNsense/CrowdSec/General.xml b/security/crowdsec/src/opnsense/mvc/app/models/OPNsense/CrowdSec/General.xml index c647de7f3..fb2d210e3 100644 --- a/security/crowdsec/src/opnsense/mvc/app/models/OPNsense/CrowdSec/General.xml +++ b/security/crowdsec/src/opnsense/mvc/app/models/OPNsense/CrowdSec/General.xml @@ -1,7 +1,7 @@ //OPNsense/crowdsec/general CrowdSec general configuration - 1.0.2 + 1.0.4 diff --git a/security/maltrail/src/opnsense/mvc/app/controllers/OPNsense/Maltrail/forms/sensor.xml b/security/maltrail/src/opnsense/mvc/app/controllers/OPNsense/Maltrail/forms/sensor.xml index 754a3b392..c875b562c 100644 --- a/security/maltrail/src/opnsense/mvc/app/controllers/OPNsense/Maltrail/forms/sensor.xml +++ b/security/maltrail/src/opnsense/mvc/app/controllers/OPNsense/Maltrail/forms/sensor.xml @@ -27,7 +27,7 @@ sensor.remoteport text - Port of the logging server. Leave empty when sensor and server run on the same system. + Port of the logging server. sensor.syslogserver diff --git a/security/stunnel/Makefile b/security/stunnel/Makefile index 2f7b74f0c..d771947df 100644 --- a/security/stunnel/Makefile +++ b/security/stunnel/Makefile @@ -1,6 +1,6 @@ PLUGIN_NAME= stunnel PLUGIN_VERSION= 1.0.5 -PLUGIN_REVISION= 1 +PLUGIN_REVISION= 2 PLUGIN_COMMENT= Stunnel TLS proxy PLUGIN_MAINTAINER= ad@opnsense.org PLUGIN_DEPENDS= stunnel diff --git a/security/stunnel/src/etc/inc/plugins.inc.d/stunnel.inc b/security/stunnel/src/etc/inc/plugins.inc.d/stunnel.inc index ad0a37973..af102b1e8 100644 --- a/security/stunnel/src/etc/inc/plugins.inc.d/stunnel.inc +++ b/security/stunnel/src/etc/inc/plugins.inc.d/stunnel.inc @@ -94,24 +94,14 @@ function stunnel_refresh_crls() proc_close($process); } if ($ca_hash) { - $crlres = openssl_crl_new($ca_crt, 0, 9999); if (!empty($configObj->crl)) { foreach ($configObj->crl as $crl) { - if ($crl->caref == $cacert && !empty((string)$crl->cert)) { - foreach ($crl->cert as $cert) { - openssl_crl_revoke_cert( - $crlres, - base64_decode((string)$cert->crt), - (string)$cert->revoke_time, - (string)$cert->reason - ); - } + if ($crl->caref == $cacert && !empty((string)$crl->text)) { + file_put_contents("/var/run/stunnel/certs/{$ca_hash}.r0", (string)$crl->text); + break; } } } - $crl_text = ""; - openssl_crl_export($crlres, $crl_text, $ca_key); - file_put_contents("/var/run/stunnel/certs/{$ca_hash}.r0", $crl_text); } } } diff --git a/www/nginx/Makefile b/www/nginx/Makefile index 061ff42aa..92f09f009 100644 --- a/www/nginx/Makefile +++ b/www/nginx/Makefile @@ -1,5 +1,5 @@ PLUGIN_NAME= nginx -PLUGIN_VERSION= 1.31 +PLUGIN_VERSION= 1.32 PLUGIN_COMMENT= Nginx HTTP server and reverse proxy PLUGIN_DEPENDS= nginx PLUGIN_MAINTAINER= franz.fabian.94@gmail.com diff --git a/www/nginx/pkg-descr b/www/nginx/pkg-descr index 2633ad5d6..f05159d2b 100644 --- a/www/nginx/pkg-descr +++ b/www/nginx/pkg-descr @@ -10,6 +10,25 @@ WWW: https://nginx.org/ Plugin Changelog ================ +1.32 + +* add support for resetting timed out connections and 444 responses (reset_timedout_connection) +* add option to change autoban response code to 444 (NGX_HTTP_CLOSE) +* add ssl_reject_handshake directive support +* add error log severity level support for HTTP and Stream servers +* add logging of possible errors causes to the setup script +* migrate general error log to syslog +* $internalModelUseSafeDelete enabled in API Settings controller to check item references before delete +* minor style adjustments for IP ACL and SNI Based Routing forms +* handle possible remaining vts socket after nginx start failure +* add uuid columns to the grids and a button to copy its value to clipboard +* add Cache Path columns naming +* fix: add the PROXY protocol for the HTTPS listener too, if it is set for the server +* fix: set Stream server outbound PROXY protocol based on Upstream settings +* fix: set Trusted Proxies (set_real_ip_from) for Stream server only with PROXY protocol enabled +* fix: do not include commented out core rules in naxsi policies when importing +* fix: fixed a typo in setting the proxy_ssl_session_reuse directive value (thanks to Sigurd Våg Aaknes) + 1.31 * Allow dynamic proxy_ssl_name (contributed by Mike Reiche) diff --git a/www/nginx/src/etc/inc/plugins.inc.d/nginx.inc b/www/nginx/src/etc/inc/plugins.inc.d/nginx.inc index cf35e0613..ad9cd4ea3 100644 --- a/www/nginx/src/etc/inc/plugins.inc.d/nginx.inc +++ b/www/nginx/src/etc/inc/plugins.inc.d/nginx.inc @@ -26,6 +26,21 @@ POSSIBILITY OF SUCH DAMAGE. */ +/** + * register syslog facilities + * @return array + */ +function nginx_syslog() +{ + $syslogconf = array(); + + $syslogconf['nginx'] = array( + 'facility' => array('nginx'), + ); + + return $syslogconf; +} + function nginx_cron() { return array( diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/SettingsController.php b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/SettingsController.php index 19d73ae69..4a0849cf5 100644 --- a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/SettingsController.php +++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/Api/SettingsController.php @@ -35,6 +35,7 @@ class SettingsController extends ApiMutableModelControllerBase { protected static $internalModelClass = '\OPNsense\Nginx\Nginx'; protected static $internalModelName = 'nginx'; + protected static $internalModelUseSafeDelete = true; // download rules public function downloadrulesAction() @@ -104,7 +105,7 @@ class SettingsController extends ApiMutableModelControllerBase // Upstream public function searchupstreamAction() { - return $this->searchBase('upstream', array('description', 'serverentries', 'tls_enable', 'load_balancing_algorithm')); + return $this->searchBase('upstream', array('uuid', 'description', 'serverentries', 'tls_enable', 'load_balancing_algorithm')); } public function getupstreamAction($uuid = null) @@ -131,7 +132,7 @@ class SettingsController extends ApiMutableModelControllerBase // Upstream Server public function searchupstreamserverAction() { - return $this->searchBase('upstream_server', array('description', 'server', 'port', 'priority')); + return $this->searchBase('upstream_server', array('uuid', 'description', 'server', 'port', 'priority')); } public function getupstreamserverAction($uuid = null) @@ -159,8 +160,8 @@ class SettingsController extends ApiMutableModelControllerBase public function searchlocationAction() { $data = $this->searchBase('location', array( - 'description','urlpattern', 'path_prefix', 'matchtype', 'upstream', - 'enable_secrules', 'enable_learning_mode', 'force_https', + 'uuid', 'description', 'urlpattern', 'path_prefix', 'matchtype', + 'upstream', 'enable_secrules', 'enable_learning_mode', 'force_https', 'xss_block_score', 'sqli_block_score', 'custom_policy' )); @@ -232,7 +233,7 @@ class SettingsController extends ApiMutableModelControllerBase public function searchhttpserverAction() { return $this->searchBase('http_server', array( - 'servername', 'locations', 'root', 'https_only', 'certificate', + 'uuid', 'servername', 'locations', 'root', 'https_only', 'certificate', 'listen_http_address', 'listen_https_address', 'default_server' )); } @@ -261,7 +262,7 @@ class SettingsController extends ApiMutableModelControllerBase // stream server public function searchstreamserverAction() { - return $this->searchBase('stream_server', array('description', 'certificate', 'udp', 'listen_address')); + return $this->searchBase('stream_server', array('uuid', 'description', 'certificate', 'udp', 'listen_address')); } public function getstreamserverAction($uuid = null) diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httprewrite.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httprewrite.xml index eda6fc20c..fbc06bb5b 100644 --- a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httprewrite.xml +++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httprewrite.xml @@ -22,6 +22,6 @@ dropdown - Stop rule processing (break) and perform (internal) redirect (last). Return a moved permanently status code (301) or a teporary redirect (302). + Stop rule processing (break) or perform (internal) redirect (last). Return a moved permanently status code (301) or a temporary redirect (302). diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httpserver.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httpserver.xml index e4a925509..73e6aff5c 100644 --- a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httpserver.xml +++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/httpserver.xml @@ -20,6 +20,13 @@ checkbox + + httpserver.tls_reject_handshake + + checkbox + If enabled, TLS handshakes for this server will be rejected. + true + httpserver.syslog_targets @@ -127,6 +134,14 @@ dropdown + + httpserver.error_log_level + + + dropdown + Select Error Log Level. Log levels are listed in the order of increasing verbosity. Setting a certain log level will cause all messages of the specified and more severe log levels to be logged. + true + httpserver.enable_acme_support diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/settings.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/settings.xml index 4da550ab1..e5d55040d 100644 --- a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/settings.xml +++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/settings.xml @@ -42,6 +42,12 @@ text After this idle time, the client gets disconnected. + + nginx.http.reset_timedout + + checkbox + Reset timed out connections and connections closed with the non-standard code 444. When the socket is closed, TCP RST is sent to the client, and all memory occupied by this socket is released. This helps avoid keeping an already closed socket with filled buffers in a FIN_WAIT1 state for a long time. + nginx.http.default_type @@ -60,6 +66,14 @@ text true + + nginx.http.ban_response + + + dropdown + Select a response code for auto-blocking requests (bot user-agent or honeypot location). The default code is 403. 444 is a special response code that closes the connection without a response to the client. + true + nginx.http.headers_more_enable diff --git a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/streamserver.xml b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/streamserver.xml index b988ecbc4..a243578dd 100644 --- a/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/streamserver.xml +++ b/www/nginx/src/opnsense/mvc/app/controllers/OPNsense/Nginx/forms/streamserver.xml @@ -63,6 +63,14 @@ dropdown + + streamserver.error_log_level + + + dropdown + Select Error Log Level. Log levels are listed in the order of increasing verbosity. Setting a certain log level will cause all messages of the specified and more severe log levels to be logged. + true + streamserver.route_field diff --git a/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Menu/Menu.xml b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Menu/Menu.xml index bbe625bd7..74100456f 100644 --- a/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Menu/Menu.xml +++ b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Menu/Menu.xml @@ -9,7 +9,7 @@ - + diff --git a/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Nginx.xml b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Nginx.xml index 46cd6dbd3..cf4487baa 100644 --- a/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Nginx.xml +++ b/www/nginx/src/opnsense/mvc/app/models/OPNsense/Nginx/Nginx.xml @@ -1,6 +1,6 @@ //OPNsense/Nginx - 1.30 + 1.32 nginx web server, reverse proxy and waf @@ -41,6 +41,10 @@ 60 N + + 0 + Y + N @@ -52,6 +56,15 @@ N 1 + + N + + 403 Forbidden + 444 Terminate Connection + + Y + 403 + N @@ -698,6 +711,10 @@ + + 0 + Y + 0 Y @@ -786,6 +803,20 @@ Y + + N + + Emergency + Alert + Critical + Error (default) + Warning + Notice + Informational + + Y + error + Y 1 @@ -1004,6 +1035,20 @@ Y + + N + + Emergency + Alert + Critical + Error + Warning + Notice + Informational (default) + + Y + info + upstream diff --git a/www/nginx/src/opnsense/mvc/app/views/OPNsense/Nginx/index.volt b/www/nginx/src/opnsense/mvc/app/views/OPNsense/Nginx/index.volt index eacb21ea1..7a876e42c 100644 --- a/www/nginx/src/opnsense/mvc/app/views/OPNsense/Nginx/index.volt +++ b/www/nginx/src/opnsense/mvc/app/views/OPNsense/Nginx/index.volt @@ -73,6 +73,10 @@ #frm_ipacl_dlg .col-md-5 { width: 25%; } + #row_snihostname\.data .row, + #row_ipacl\.data .row { + padding-top: 5px; + } #row_snihostname\.data .row div, #row_ipacl\.data .row div { padding: 0; @@ -81,6 +85,9 @@ #frm_ipacl_dlg .bootstrap-select { width: 100% !important; } + .filter-option { + padding: inherit !important; + } @@ -216,17 +223,18 @@ - - - - - - - - - - - + + + + + + + + + + + + @@ -247,11 +255,12 @@
{{ lang._('Description') }}{{ lang._('URL Pattern') }}{{ lang._('URL Path Prefix') }}{{ lang._('Match Type') }}{{ lang._('Upstream') }}{{ lang._('WAF Status') }}{{ lang._('XSS Score') }}{{ lang._('SQLi Score') }}{{ lang._('WAF Policies') }}{{ lang._('Force HTTPS') }}{{ lang._('Commands') }}{{ lang._('ID') }}{{ lang._('Description') }}{{ lang._('URL Pattern') }}{{ lang._('URL Path Prefix') }}{{ lang._('Match Type') }}{{ lang._('Upstream') }}{{ lang._('WAF Status') }}{{ lang._('XSS Score') }}{{ lang._('SQLi Score') }}{{ lang._('WAF Policies') }}{{ lang._('Force HTTPS') }}{{ lang._('Commands') }}
- - - - - + + + + + + @@ -273,11 +282,12 @@
{{ lang._('Description') }}{{ lang._('Server') }}{{ lang._('Port') }}{{ lang._('Priority') }}{{ lang._('Commands') }}{{ lang._('ID') }}{{ lang._('Description') }}{{ lang._('Server') }}{{ lang._('Port') }}{{ lang._('Priority') }}{{ lang._('Commands') }}
- - - - - + + + + + + @@ -297,8 +307,8 @@
{{ lang._('Description') }}{{ lang._('Servers') }}{{ lang._('Load Balancing') }}{{ lang._('TLS Enabled') }}{{ lang._('Commands') }}{{ lang._('ID') }}{{ lang._('Description') }}{{ lang._('Servers') }}{{ lang._('Load Balancing') }}{{ lang._('TLS Enabled') }}{{ lang._('Commands') }}
- - + + @@ -320,7 +330,7 @@ - + @@ -340,15 +350,16 @@
{{ lang._('Username') }}{{ lang._('Commands') }}{{ lang._('Username') }}{{ lang._('Commands') }}
{{ lang._('Name') }} {{ lang._('Users') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
+ - + - - + + @@ -368,10 +379,11 @@
{{ lang._('ID') }} {{ lang._('Servername') }} {{ lang._('Locations') }} {{ lang._('File System Root') }} {{ lang._('Certificate') }}{{ lang._('HTTPS Only') }}{{ lang._('HTTPS Only') }} {{ lang._('HTTP Address') }} {{ lang._('HTTPS Address') }}{{ lang._('Default') }}{{ lang._('Commands') }}{{ lang._('Default') }}{{ lang._('Commands') }}
+ - + - + @@ -392,10 +404,10 @@ - + - + @@ -426,11 +438,11 @@ - + - + @@ -450,14 +462,14 @@
{{ lang._('ID') }} {{ lang._('Certificate') }}{{ lang._('UDP') }}{{ lang._('UDP') }} {{ lang._('Address') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('Description') }}{{ lang._('Source URL') }}{{ lang._('Source URL') }} {{ lang._('Destination URL') }} {{ lang._('Flag') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('Name') }}{{ lang._('Operator') }}{{ lang._('Operator') }} {{ lang._('Value') }} {{ lang._('Rules') }} {{ lang._('Action') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
+ - + - - + - + @@ -483,7 +495,7 @@ - + @@ -504,10 +516,10 @@ - - - - + + + + @@ -532,7 +544,7 @@ - + @@ -557,7 +569,7 @@ - + @@ -578,7 +590,7 @@ - + @@ -599,7 +611,7 @@ - + @@ -622,7 +634,7 @@ - + @@ -643,7 +655,7 @@ - + @@ -667,7 +679,7 @@ - + diff --git a/www/nginx/src/opnsense/scripts/nginx/naxsi_rule_download.php b/www/nginx/src/opnsense/scripts/nginx/naxsi_rule_download.php index 3eca0a62e..0ef130059 100755 --- a/www/nginx/src/opnsense/scripts/nginx/naxsi_rule_download.php +++ b/www/nginx/src/opnsense/scripts/nginx/naxsi_rule_download.php @@ -100,8 +100,13 @@ function save_to_model($data) $policy->action = 'BLOCK'; // create new values for policy $rule_list = []; + $dis_rules = []; foreach ($rules as $rule) { $rule_mdl = $model->naxsi_rule->Add(); + // exclude commented rules from policy + if (str_starts_with($rule['rule'], '#')) { + $dis_rules[] = (string)$rule_mdl->getAttributes()["uuid"]; + } $rule_mdl->description = $rule['message']; $rule_mdl->message = $rule['message']; $rule_mdl->ruletype = 'main'; @@ -160,9 +165,8 @@ function save_to_model($data) } $rule_list[] = $rule_mdl->getAttributes()["uuid"]; } - $policy->naxsi_rules = implode(',', $rule_list); + $policy->naxsi_rules = implode(',', array_diff($rule_list, $dis_rules)); } - $val_result = $model->performValidation(false); if (count($val_result) !== 0) { print_r($val_result); diff --git a/www/nginx/src/opnsense/scripts/nginx/setup.php b/www/nginx/src/opnsense/scripts/nginx/setup.php index f700a23e4..1b9a304fd 100755 --- a/www/nginx/src/opnsense/scripts/nginx/setup.php +++ b/www/nginx/src/opnsense/scripts/nginx/setup.php @@ -31,6 +31,7 @@ const KEY_DIRECTORY = '/usr/local/etc/nginx/key/'; const GROUP_OWNER = 'staff'; require_once('config.inc'); require_once('certs.inc'); +require_once('util.inc'); use OPNsense\Nginx\Nginx; function export_pem_file($filename, $data, $post_append = null) @@ -68,12 +69,20 @@ function find_ca($refid) if (!isset($config['OPNsense']['Nginx'])) { die("nginx is not configured"); } +openlog("nginx", LOG_ODELAY, LOG_USER); +syslog(LOG_DEBUG, "NGINX setup routine started."); @mkdir('/usr/local/etc/nginx/key', 0750, true); @mkdir("/var/db/nginx/auth", 0750, true); @mkdir("/var/log/nginx", 0750, true); @chgrp('/var/db/nginx', GROUP_OWNER); @chgrp('/var/db/nginx/auth', GROUP_OWNER); @chgrp('/var/log/nginx', GROUP_OWNER); +// unlink VTS socket if nginx didn't +$vts_socket = '/var/run/nginx_status.sock'; +if (!isvalidpid('/var/run/nginx.pid') && file_exists($vts_socket)) { + syslog(LOG_WARNING, "NGINX setup: nginx not running but VTS socket exists. Unlinking."); + @unlink($vts_socket); +} $nginx = $config['OPNsense']['Nginx']; if (isset($nginx['http_server'])) { if (is_array($nginx['http_server']) && !isset($nginx['http_server']['servername'])) { @@ -84,8 +93,10 @@ if (isset($nginx['http_server'])) { foreach ($http_servers as $http_server) { if (!empty($http_server['listen_https_address']) && !empty($http_server['certificate'])) { // try to find the reference + $hostname = explode(',', $http_server['servername'])[0]; $cert = find_cert($http_server['certificate']); if (!isset($cert)) { + syslog(LOG_ERR, "NGINX setup: Certificate is set but not found in config for server {$hostname}."); continue; } $chain = []; @@ -94,8 +105,9 @@ if (isset($nginx['http_server'])) { foreach ($ca_chain as $entry) { $chain[] = base64_decode($entry['crt']); } + } else { + syslog(LOG_WARNING, "NGINX setup: Certificate chain is empty for server {$hostname}."); } - $hostname = explode(',', $http_server['servername'])[0]; export_pem_file( KEY_DIRECTORY . $hostname . '.pem', $cert['crt'], @@ -131,6 +143,7 @@ if (isset($nginx['stream_server'])) { // try to find the reference $cert = find_cert($stream_server['certificate']); if (!isset($cert)) { + syslog(LOG_ERR, "NGINX setup: Certificate is set but not found in config for stream server {$stream_server['listen_address']}."); continue; } $chain = []; @@ -139,6 +152,8 @@ if (isset($nginx['stream_server'])) { foreach ($ca_chain as $entry) { $chain[] = base64_decode($entry['crt']); } + } else { + syslog(LOG_WARNING, "NGINX setup: Certificate chain is empty for stream server {$stream_server['listen_address']}."); } export_pem_file( KEY_DIRECTORY . $stream_server['@attributes']['uuid'] . '.pem', @@ -194,6 +209,8 @@ if (isset($nginx['upstream'])) { KEY_DIRECTORY . $upstream['tls_client_certificate'] . '.key', $cert['prv'] ); + } else { + syslog(LOG_ERR, "NGINX setup: Client certificate is set but not found in config for upstream {$upstream['description']}."); } } if (!empty($upstream['tls_trusted_certificate'])) { @@ -203,6 +220,8 @@ if (isset($nginx['upstream'])) { $ca = find_ca($caref); if (isset($ca)) { $cas[] = base64_decode($ca['crt']); + } else { + syslog(LOG_ERR, "NGINX setup: Trusted CA certificate is set but not found in config for upstream {$upstream['description']}."); } } export_pem_file( @@ -243,6 +262,7 @@ foreach ($nginx->userlist->iterateItems() as $user_list) { // create directories for cache foreach ($nginx->cache_path->iterateItems() as $cache_path) { @mkdir((string)$cache_path->path, 0755, true); + @chgrp((string)$cache_path->path, GROUP_OWNER); } // create custom error pages @@ -319,4 +339,14 @@ file_put_contents( ); chmod('/usr/local/etc/nginx/tls_fingerprints.json', 0644); +// test config and exit early if it not good +$conf_test_errors = shell_safe('nginx -t -q 2>&1'); +if (!empty($conf_test_errors)) { + syslog(LOG_EMERG, $conf_test_errors); + closelog(); + exit(1); +} + +syslog(LOG_DEBUG, "NGINX setup routine completed."); +closelog(); passthru('/usr/local/etc/rc.d/php-fpm start'); diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf index 61b332525..a5470e6da 100644 --- a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf +++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf @@ -46,6 +46,9 @@ server_names_hash_bucket_size {{ OPNsense.Nginx.http.server_names_hash_bucket_si {% if OPNsense.Nginx.http.keepalive_timeout is defined and OPNsense.Nginx.http.keepalive_timeout != '' %} keepalive_timeout {{ OPNsense.Nginx.http.keepalive_timeout }}; {% endif %} +{% if OPNsense.Nginx.http.reset_timedout is defined and OPNsense.Nginx.http.reset_timedout == '1' %} +reset_timedout_connection on; +{% endif %} map $http_upgrade $connection_upgrade { default upgrade; @@ -105,36 +108,42 @@ server { {% endfor %} {% endif %} -{% if server.listen_https_address is defined and server.listen_https_address != '' and server.certificate is defined %} +{% if server.listen_https_address is defined and server.listen_https_address != '' %} {% for listen_address in server.listen_https_address.split(',') %} - listen {{ listen_address }} http2 ssl{% if server.default_server is defined and server.default_server == '1' %} default_server{% endif %}; + listen {{ listen_address }} http2 ssl{% if server.proxy_protocol is defined and server.proxy_protocol == '1' %} proxy_protocol{% endif %}{% if server.default_server is defined and server.default_server == '1' %} default_server{% endif %}; {% endfor %} -{% if server.ca is defined %} + +{% if server.tls_reject_handshake is defined and server.tls_reject_handshake == '1'%} + ssl_reject_handshake on; +{% endif %} +{% if server.certificate is defined %} +{% if server.ca is defined %} ssl_client_certificate /usr/local/etc/nginx/key/{{ single_servername }}_ca.pem; ssl_verify_client {{ server.verify_client }}; -{% endif %} -{% if server.zero_rtt == '1' %} +{% endif %} +{% if server.zero_rtt == '1' %} ssl_early_data on; -{% endif %} +{% endif %} ssl_certificate_key /usr/local/etc/nginx/key/{{ single_servername }}.key; ssl_certificate /usr/local/etc/nginx/key/{{ single_servername }}.pem; ssl_protocols {{ server.tls_protocols.replace(',', ' ') }}; ssl_dhparam /usr/local/opnsense/data/OPNsense/Nginx/dh-parameters.4096.rfc7919; -{% if server.tls_ciphers is defined and server.tls_ciphers != '' %} +{% if server.tls_ciphers is defined and server.tls_ciphers != '' %} ssl_ciphers {{ server.tls_ciphers }}; -{% endif %} -{% if server.tls_ecdh_curve is defined and server.tls_ecdh_curve != '' %} +{% endif %} +{% if server.tls_ecdh_curve is defined and server.tls_ecdh_curve != '' %} ssl_ecdh_curve {{ server.tls_ecdh_curve }}; -{% endif %} +{% endif %} ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_prefer_server_ciphers {% if server.tls_prefer_server_ciphers is defined and server.tls_prefer_server_ciphers == '0'%}off{% else %}on{% endif %}; -{% if server.ocsp_stapling is defined and server.ocsp_stapling == '1'%} +{% if server.ocsp_stapling is defined and server.ocsp_stapling == '1'%} ssl_stapling on; ssl_stapling_verify {% if server.ocsp_verify is defined and server.ocsp_verify == '1' %}On{% else %}Off{% endif %}; -{% else %} +{% else %} ssl_stapling off; +{% endif %} {% endif %} {% endif %} @@ -172,7 +181,7 @@ server { {% include "OPNsense/Nginx/syslog_targets.conf" %} {% endif %} access_log /var/log/nginx/tls_handshake.log handshake; - error_log /var/log/nginx/{{ server.servername }}.error.log; + error_log /var/log/nginx/{{ server.servername }}.error.log{% if server.error_log_level is defined %} {{ server.error_log_level }}{% endif %}; {% if server.root is defined and server.root != '' %} root "{{server.root}}"; {% endif %} @@ -233,9 +242,8 @@ server { location @permanentban { access_log /var/log/nginx/permanentban.access.log main; internal; - add_header Content-Type text/plain; - add_header Charset utf-8; - return 403 "You got banned permanently from this server."; + add_header "Content-Type" "text/plain; charset=UTF-8" always; + return {% if OPNsense.Nginx.http.ban_response is defined and OPNsense.Nginx.http.ban_response != '403' %}{{OPNsense.Nginx.http.ban_response}}{% else %}403 "You got banned permanently from this server."{% endif %}; } error_page 418 = @permanentban; location = /waf_denied.html { diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf index 2b468ff45..0610d4fd5 100644 --- a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf +++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf @@ -199,8 +199,8 @@ location {{ location.matchtype }} {{ location.urlpattern }} { {% if upstream.tls_protocol_versions is defined and upstream.tls_protocol_versions != '' %} proxy_ssl_protocols {{ upstream.tls_protocol_versions.replace(',', ' ') }}; {% endif %} -{% if upstream.tls_name_override is defined %} - proxy_ssl_session_reuse {% if upstream.tls_name_override != '0' %}off{% else %}on{% endif %}; +{% if upstream.tls_session_reuse is defined %} + proxy_ssl_session_reuse {% if upstream.tls_session_reuse == '1' %}on{% else %}off{% endif %}; {% endif %} {% if upstream.tls_trusted_certificate is defined and upstream.tls_trusted_certificate != '' %} proxy_ssl_trusted_certificate /usr/local/etc/nginx/key/trust_upstream_{{ location.upstream }}.pem; diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf index a140d361d..fb8f66075 100644 --- a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf +++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf @@ -12,7 +12,8 @@ load_module /usr/local/libexec/nginx/ngx_http_headers_more_filter_module.so; user www staff; worker_processes {{ OPNsense.Nginx.http.workerprocesses }}; -error_log /var/log/nginx/error.log; +#error_log /var/log/nginx/error.log; +error_log syslog:server=unix:/var/run/log,facility=local6,nohostname warn; events { worker_connections {{ OPNsense.Nginx.http.workerconnections }}; diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/streams.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/streams.conf index 3076099bc..a2d306079 100644 --- a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/streams.conf +++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/streams.conf @@ -58,7 +58,7 @@ {% set syslog_targets = server.syslog_targets.split(',') %} {% include "OPNsense/Nginx/syslog_targets.conf" %} {% endif %} - error_log /var/log/nginx/stream_{{ server['@uuid'] }}.error.log info; + error_log /var/log/nginx/stream_{{ server['@uuid'] }}.error.log {% if server.error_log_level is defined and server.error_log_level != 'info'%}{{ server.error_log_level }}{% else %}info{% endif %}; {% if server.route_field == 'sni_upstream_map' %} ssl_preread on; @@ -100,11 +100,11 @@ {% elif server.route_field == 'sni_upstream_map' %} proxy_pass $hostmap{{ server.sni_upstream_map.replace('-','') }}; {% endif %} - proxy_protocol {% if server.proxy_protocol == '1' %}on{% else %}off{% endif %}; + proxy_protocol {% if upstream.proxy_protocol == '1' %}on{% else %}off{% endif %}; {% if server.proxy_responses is defined and server.proxy_responses != '' %} proxy_responses {{ server.proxy_responses }}; {% endif%} -{% if server.trusted_proxies is defined and server.trusted_proxies != '' %} +{% if server.trusted_proxies is defined and server.trusted_proxies != '' and server.proxy_protocol is defined and server.proxy_protocol == '1' %} {% for trusted_proxy in server.trusted_proxies.split(',') %} set_real_ip_from {{ trusted_proxy }}; {% endfor %} diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Syslog/local/nginx.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Syslog/local/nginx.conf new file mode 100644 index 000000000..421c50528 --- /dev/null +++ b/www/nginx/src/opnsense/service/templates/OPNsense/Syslog/local/nginx.conf @@ -0,0 +1,6 @@ +################################################################### +# Local syslog-ng configuration filter definition [nginx]. +################################################################### +filter f_local_nginx { + program("nginx"); +}; diff --git a/www/nginx/src/opnsense/www/js/nginx/dist/configuration.min.js b/www/nginx/src/opnsense/www/js/nginx/dist/configuration.min.js index c6a86734e..e7c57eb22 100644 --- a/www/nginx/src/opnsense/www/js/nginx/dist/configuration.min.js +++ b/www/nginx/src/opnsense/www/js/nginx/dist/configuration.min.js @@ -1 +1 @@ -!function(e){var t={};function n(s){if(t[s])return t[s].exports;var i=t[s]={i:s,l:!1,exports:{}};return e[s].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,s){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(s,i,function(t){return e[t]}.bind(null,i));return s},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=27)}({27:function(e,t,n){"use strict";n.r(t);var s=Backbone.View.extend({tagName:"div",attributes:{class:"container-fluid"},child_views:[],createModel:null,upstreamCollection:null,initialize:function(e){this.dataField=$(e.dataField),this.entryclass=e.entryclass,this.createModel=e.createModel,this.upstreamCollection=e.upstreamCollection,this.listenTo(this.collection,"add remove reset",this.render),this.listenTo(this.collection,"change",this.update),this.dataField.after(this.$el)},events:{"click .add":"addEntry"},render:function(){this.child_views.forEach(e=>e.remove()),this.$el.html(""),this.child_views=[],this.update(),this.collection.each(e=>{const t=new this.entryclass({model:e,collection:this.collection,upstreamCollection:this.upstreamCollection});this.child_views.push(t),this.$el.append(t.$el),t.render()}),this.$el.append($('\n
\n \n
'))},update:function(){this.dataField.data("data",this.collection.toJSON())},addEntry:function(e){e.preventDefault(),this.collection.add(this.createModel())}});var i=Backbone.Collection.extend({url:"/api/nginx/settings/searchupstream",parse:function(e){return e.rows}});const a=Backbone.View.extend({tagName:"div",attributes:{class:"row"},events:{"keyup .key":function(){this.model.set("hostname",this.key.value)},"change .value":function(){this.model.set("upstream",this.value.value)},"click .delete":"deleteEntry"},key:null,value:null,delBtn:null,first:null,second:null,third:null,upstreamCollection:null,initialize:function(e){this.upstreamCollection=e.upstreamCollection,this.listenTo(this.upstreamCollection,"update reset add remove",this.regenerate_list),this.first=document.createElement("div"),this.first.classList.add("col-sm-5"),this.key=document.createElement("input"),this.first.append(this.key),this.key.type="text",this.key.classList.add("key"),this.key.value=this.model.get("hostname"),this.second=document.createElement("div"),this.second.classList.add("col-sm-5"),this.value=document.createElement("select"),this.second.append(this.value),this.value.classList.add("value"),this.value.classList.add("form-control"),this.value.value=this.model.get("upstream"),this.third=document.createElement("div"),this.third.classList.add("col-sm-2"),this.third.style.textAlign="right",this.delBtn=document.createElement("button"),this.delBtn.classList.add("delete"),this.delBtn.classList.add("btn"),this.delBtn.innerHTML='',this.third.append(this.delBtn),this.model.has("upstream")&&0!==this.upstreamCollection.where({uuid:this.model.get("upstream")}).length||this.upstreamCollection.length>0&&this.model.set("upstream",this.upstreamCollection.at(0).get("uuid")),this.$el.append(this.first).append(this.second).append(this.third)},render:function(){$(this.key).val(this.model.get("hostname")),this.regenerate_list(),$(this.value).val(this.model.get("upstream"))},deleteEntry:function(e){e.preventDefault(),this.collection.remove(this.model)},regenerate_list:function(){const e=$(this.value);e.html(""),this.upstreamCollection.each(t=>e.append(``)),e.val(this.model.get("upstream")),e.selectpicker("refresh")}}),l=Backbone.View.extend({tagName:"div",attributes:{class:"row"},events:{"keyup .key":function(){this.model.set("network",this.key.value)},"change .value":function(){this.model.set("action",this.value.value)},"click .delete":"deleteEntry"},key:null,value:null,delBtn:null,first:null,second:null,third:null,upstreamCollection:null,initialize:function(e){this.upstreamCollection=e.upstreamCollection,this.listenTo(this.upstreamCollection,"update reset add remove",this.regenerate_list),this.first=document.createElement("div"),this.first.classList.add("col-sm-5"),this.key=document.createElement("input"),this.first.append(this.key),this.key.type="text",this.key.classList.add("key"),this.key.value=this.model.get("network"),this.second=document.createElement("div"),this.second.classList.add("col-sm-5"),this.value=document.createElement("select"),this.second.append(this.value),this.value.classList.add("value"),this.value.classList.add("form-control"),this.value.value=this.model.get("action"),this.third=document.createElement("div"),this.third.classList.add("col-sm-2"),this.third.style.textAlign="right",this.delBtn=document.createElement("button"),this.delBtn.classList.add("delete"),this.delBtn.classList.add("btn"),this.delBtn.innerHTML='',this.third.append(this.delBtn),this.$el.append(this.first).append(this.second).append(this.third)},render:function(){$(this.key).val(this.model.get("network")),this.regenerate_list(),$(this.value).val(this.model.get("action"))},deleteEntry:function(e){e.preventDefault(),this.collection.remove(this.model)},regenerate_list:function(){const e=$(this.value);e.html(""),this.upstreamCollection.each(t=>e.append(``)),e.val(this.model.get("action")),e.selectpicker("refresh")}});var o=Backbone.Collection.extend({initialize:function(){let e=this;$("#snihostname\\.data").change(function(){e.regenerateFromView()})},regenerateFromView:function(){let e=$("#snihostname\\.data").data("data");_.isArray(e)||(e=[]),this.reset(e)}}),r=Backbone.Model.extend({}),c=Backbone.Model.extend({}),d=Backbone.Collection.extend({initialize:function(){let e=this;$("#ipacl\\.data").change(function(){e.regenerateFromView()})},regenerateFromView:function(){let e=$("#ipacl\\.data").data("data");_.isArray(e)||(e=[]),this.reset(e)}});const u=new i,h=new Backbone.Collection([{name:"Deny",value:"deny"},{name:"Allow",value:"allow"}]);$(document).ready(function(){mapDataToFormUI({frm_nginx:"/api/nginx/settings/get"}).done(function(){formatTokenizersUI(),$('select[data-allownew="false"]').selectpicker("refresh"),updateServiceControlUI("nginx")}),""!==window.location.hash&&$('a[href="'+window.location.hash+'"]').click(),$(".nav-tabs a").on("shown.bs.tab",function(e){history.pushState(null,null,e.target.hash)}),$(".reload_btn").click(function(){$(".reloadAct_progress").addClass("fa-spin"),ajaxCall("/api/nginx/service/reconfigure",{},function(){$(".reloadAct_progress").removeClass("fa-spin")})}),$('[id*="save_"]').each(function(){$(this).click(function(){let e=$(this).closest("form").attr("id"),t=$(this).closest("form").attr("data-title");saveFormToEndpoint("/api/nginx/settings/set",e,function(){$("#"+e+"_progress").addClass("fa fa-spinner fa-pulse"),ajaxCall("/api/nginx/service/reconfigure",{},function(n,s){$("#"+e+"_progress").removeClass("fa fa-spinner fa-pulse"),void 0===n||"success"===s&&"ok"===n.status?updateServiceControlUI("nginx"):BootstrapDialog.show({type:BootstrapDialog.TYPE_WARNING,title:t,message:JSON.stringify(n),draggable:!0})})})})}),["upstream","upstreamserver","location","credential","userlist","httpserver","streamserver","httprewrite","custompolicy","security_header","ipacl","limit_zone","cache_path","limit_request_connection","snifwd","errorpage","tls_fingerprint","syslog_target","naxsirule"].forEach(function(e){$("#grid-"+e).UIBootgrid({search:"/api/nginx/settings/search"+e,get:"/api/nginx/settings/get"+e+"/",set:"/api/nginx/settings/set"+e+"/",add:"/api/nginx/settings/add"+e+"/",del:"/api/nginx/settings/del"+e+"/",options:{selection:!1,multiSelect:!1,formatters:{commands:function(e,t){return''},response:function(e,t){return"none"==t.response?"unchanged":t.response},statuscodes:function(e,t){const n=[],s=t.statuscodes.split(",");for(let e of s)n.push(e.substr(0,3));return n.join(", ")}}}})}),bind_naxsi_rule_dl_button(),function(){let e=new s({dataField:document.getElementById("snihostname.data"),upstreamCollection:u,entryclass:a,collection:new o,createModel:function(){return new r({hostname:"localhost"})}});window.snifield=e,e.render(),$("#grid-upstream").on("loaded.rs.jquery.bootgrid",function(){u.fetch()}),u.fetch()}();let e=new s({dataField:document.getElementById("ipacl.data"),upstreamCollection:h,entryclass:l,collection:new d,createModel:function(){return new c({network:"::",action:"deny"})}});window.ipaclfield=e,e.render()})}}); \ No newline at end of file +!function(t){var e={};function n(i){if(e[i])return e[i].exports;var s=e[i]={i:i,l:!1,exports:{}};return t[i].call(s.exports,s,s.exports,n),s.l=!0,s.exports}n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var s in t)n.d(i,s,function(e){return t[e]}.bind(null,s));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=27)}({27:function(t,e,n){"use strict";n.r(e);var i=Backbone.View.extend({tagName:"div",attributes:{class:"container-fluid"},child_views:[],createModel:null,upstreamCollection:null,initialize:function(t){this.dataField=$(t.dataField),this.entryclass=t.entryclass,this.createModel=t.createModel,this.upstreamCollection=t.upstreamCollection,this.listenTo(this.collection,"add remove reset",this.render),this.listenTo(this.collection,"change",this.update),this.dataField.after(this.$el)},events:{"click .add":"addEntry"},render:function(){this.child_views.forEach(t=>t.remove()),this.$el.html(""),this.child_views=[],this.update(),this.collection.each(t=>{const e=new this.entryclass({model:t,collection:this.collection,upstreamCollection:this.upstreamCollection});this.child_views.push(e),this.$el.append(e.$el),e.render()}),this.$el.append($('\n
\n \n
'))},update:function(){this.dataField.data("data",this.collection.toJSON())},addEntry:function(t){t.preventDefault(),this.collection.add(this.createModel())}});var s=Backbone.Collection.extend({url:"/api/nginx/settings/searchupstream",parse:function(t){return t.rows}});const a=Backbone.View.extend({tagName:"div",attributes:{class:"row"},events:{"keyup .key":function(){this.model.set("hostname",this.key.value)},"change .value":function(){this.model.set("upstream",this.value.value)},"click .delete":"deleteEntry"},key:null,value:null,delBtn:null,first:null,second:null,third:null,upstreamCollection:null,initialize:function(t){this.upstreamCollection=t.upstreamCollection,this.listenTo(this.upstreamCollection,"update reset add remove",this.regenerate_list),this.first=document.createElement("div"),this.first.classList.add("col-sm-5"),this.key=document.createElement("input"),this.first.append(this.key),this.key.type="text",this.key.classList.add("key"),this.key.value=this.model.get("hostname"),this.second=document.createElement("div"),this.second.classList.add("col-sm-5"),this.value=document.createElement("select"),this.second.append(this.value),this.value.classList.add("value"),this.value.classList.add("form-control"),this.value.value=this.model.get("upstream"),this.third=document.createElement("div"),this.third.classList.add("col-sm-2"),this.third.style.textAlign="right",this.delBtn=document.createElement("button"),this.delBtn.classList.add("delete"),this.delBtn.classList.add("btn"),this.delBtn.innerHTML='',this.third.append(this.delBtn),this.model.has("upstream")&&0!==this.upstreamCollection.where({uuid:this.model.get("upstream")}).length||this.upstreamCollection.length>0&&this.model.set("upstream",this.upstreamCollection.at(0).get("uuid")),this.$el.append(this.first).append(this.second).append(this.third)},render:function(){$(this.key).val(this.model.get("hostname")),this.regenerate_list(),$(this.value).val(this.model.get("upstream"))},deleteEntry:function(t){t.preventDefault(),this.collection.remove(this.model)},regenerate_list:function(){const t=$(this.value);t.html(""),this.upstreamCollection.each(e=>t.append(``)),t.val(this.model.get("upstream")),t.selectpicker("refresh")}}),l=Backbone.View.extend({tagName:"div",attributes:{class:"row"},events:{"keyup .key":function(){this.model.set("network",this.key.value)},"change .value":function(){this.model.set("action",this.value.value)},"click .delete":"deleteEntry"},key:null,value:null,delBtn:null,first:null,second:null,third:null,upstreamCollection:null,initialize:function(t){this.upstreamCollection=t.upstreamCollection,this.listenTo(this.upstreamCollection,"update reset add remove",this.regenerate_list),this.first=document.createElement("div"),this.first.classList.add("col-sm-5"),this.key=document.createElement("input"),this.first.append(this.key),this.key.type="text",this.key.classList.add("key"),this.key.value=this.model.get("network"),this.second=document.createElement("div"),this.second.classList.add("col-sm-5"),this.value=document.createElement("select"),this.second.append(this.value),this.value.classList.add("value"),this.value.classList.add("form-control"),this.value.value=this.model.get("action"),this.third=document.createElement("div"),this.third.classList.add("col-sm-2"),this.third.style.textAlign="right",this.delBtn=document.createElement("button"),this.delBtn.classList.add("delete"),this.delBtn.classList.add("btn"),this.delBtn.innerHTML='',this.third.append(this.delBtn),this.$el.append(this.first).append(this.second).append(this.third)},render:function(){$(this.key).val(this.model.get("network")),this.regenerate_list(),$(this.value).val(this.model.get("action"))},deleteEntry:function(t){t.preventDefault(),this.collection.remove(this.model)},regenerate_list:function(){const t=$(this.value);t.html(""),this.upstreamCollection.each(e=>t.append(``)),t.val(this.model.get("action")),t.selectpicker("refresh")}});var o=Backbone.Collection.extend({initialize:function(){let t=this;$("#snihostname\\.data").change(function(){t.regenerateFromView()})},regenerateFromView:function(){let t=$("#snihostname\\.data").data("data");_.isArray(t)||(t=[]),this.reset(t)}}),r=Backbone.Model.extend({}),d=Backbone.Model.extend({}),c=Backbone.Collection.extend({initialize:function(){let t=this;$("#ipacl\\.data").change(function(){t.regenerateFromView()})},regenerateFromView:function(){let t=$("#ipacl\\.data").data("data");_.isArray(t)||(t=[]),this.reset(t)}});const u=new s,h=new Backbone.Collection([{name:"Deny",value:"deny"},{name:"Allow",value:"allow"}]);$(document).ready(function(){mapDataToFormUI({frm_nginx:"/api/nginx/settings/get"}).done(function(){formatTokenizersUI(),$('select[data-allownew="false"]').selectpicker("refresh"),updateServiceControlUI("nginx")}),""!==window.location.hash&&$('a[href="'+window.location.hash+'"]').click(),$(".nav-tabs a").on("shown.bs.tab",function(t){history.pushState(null,null,t.target.hash)}),$(".reload_btn").click(function(){$(".reloadAct_progress").addClass("fa-spin"),ajaxCall("/api/nginx/service/reconfigure",{},function(){$(".reloadAct_progress").removeClass("fa-spin")})}),$('[id*="save_"]').each(function(){$(this).click(function(){let t=$(this).closest("form").attr("id"),e=$(this).closest("form").attr("data-title");saveFormToEndpoint("/api/nginx/settings/set",t,function(){$("#"+t+"_progress").addClass("fa fa-spinner fa-pulse"),ajaxCall("/api/nginx/service/reconfigure",{},function(n,i){$("#"+t+"_progress").removeClass("fa fa-spinner fa-pulse"),void 0===n||"success"===i&&"ok"===n.status?updateServiceControlUI("nginx"):BootstrapDialog.show({type:BootstrapDialog.TYPE_WARNING,title:e,message:JSON.stringify(n),draggable:!0})})})})}),["upstream","upstreamserver","location","credential","userlist","httpserver","streamserver","httprewrite","custompolicy","security_header","ipacl","limit_zone","cache_path","limit_request_connection","snifwd","errorpage","tls_fingerprint","syslog_target","naxsirule"].forEach(function(t){$("#grid-"+t).UIBootgrid({search:"/api/nginx/settings/search"+t,get:"/api/nginx/settings/get"+t+"/",set:"/api/nginx/settings/set"+t+"/",add:"/api/nginx/settings/add"+t+"/",del:"/api/nginx/settings/del"+t+"/",commands:{copy_uuid:{method:function(t){navigator.clipboard.writeText($(this).data("row-id"))}}},options:{selection:!1,multiSelect:!1,formatters:{commands:function(t,e){return''},response:function(t,e){return"none"==e.response?"unchanged":e.response},statuscodes:function(t,e){const n=[],i=e.statuscodes.split(",");for(let t of i)n.push(t.substr(0,3));return n.join(", ")}}}})}),bind_naxsi_rule_dl_button(),function(){let t=new i({dataField:document.getElementById("snihostname.data"),upstreamCollection:u,entryclass:a,collection:new o,createModel:function(){return new r({hostname:"localhost"})}});window.snifield=t,t.render(),$("#grid-upstream").on("loaded.rs.jquery.bootgrid",function(){u.fetch()}),u.fetch()}();let t=new i({dataField:document.getElementById("ipacl.data"),upstreamCollection:h,entryclass:l,collection:new c,createModel:function(){return new d({network:"::",action:"deny"})}});window.ipaclfield=t,t.render()})}}); \ No newline at end of file diff --git a/www/nginx/src/opnsense/www/js/nginx/src/nginx_config.js b/www/nginx/src/opnsense/www/js/nginx/src/nginx_config.js index 0e69aaf17..c2719990e 100644 --- a/www/nginx/src/opnsense/www/js/nginx/src/nginx_config.js +++ b/www/nginx/src/opnsense/www/js/nginx/src/nginx_config.js @@ -79,14 +79,20 @@ function init_grids() { 'set': '/api/nginx/settings/set' + element + '/', 'add': '/api/nginx/settings/add' + element + '/', 'del': '/api/nginx/settings/del' + element + '/', + 'commands': { + copy_uuid: { + method: function(e) { navigator.clipboard.writeText($(this).data("row-id")); } + } + }, 'options': { selection: false, multiSelect: false, formatters: { "commands": function (column, row) { - return " " + - "" + - ""; + return " " + + " " + + " " + + ""; }, "response": function (column, row) { return ((row.response == "none") ? "unchanged" : row.response);
{{ lang._('ID') }} {{ lang._('Description') }}{{ lang._('Rule Type') }}{{ lang._('Rule Type') }} {{ lang._('Match Type') }}{{ lang._('ID') }}{{ lang._('Score') }}{{ lang._('Score') }} {{ lang._('Value') }} {{ lang._('Message') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('HSTS') }} {{ lang._('CSP') }} {{ lang._('CSP Rules') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('Path') }}{{ lang._('Description') }}{{ lang._('Description') }}{{ lang._('Description') }}{{ lang._('Commands') }}{{ lang._('Size') }}{{ lang._('Inactive') }}{{ lang._('Max Size') }}{{ lang._('Commands') }}
{{ lang._('Size') }} {{ lang._('Rate') }} {{ lang._('Rate Unit') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('Connection Count') }} {{ lang._('Burst') }} {{ lang._('No Delay') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('Description') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('Description') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('Name') }} {{ lang._('Status Codes') }} {{ lang._('Response') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('Description') }}{{ lang._('Commands') }}{{ lang._('Commands') }}
{{ lang._('Host') }} {{ lang._('Facility') }} {{ lang._('Severity') }}{{ lang._('Commands') }}{{ lang._('Commands') }}