mirror of
https://github.com/opnsense/plugins.git
synced 2026-05-28 04:34:15 -04:00
Merge branch 'master' into fix/DNSHostinger
This commit is contained in:
commit
d814f18965
133 changed files with 2750 additions and 2110 deletions
28
.github/pull_request_template.md
vendored
Normal file
28
.github/pull_request_template.md
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
**Important notices**
|
||||
Before you submit a pull request, we ask you kindly to acknowledge the following:
|
||||
|
||||
- [ ] I have read the contributing guidelines at https://github.com/opnsense/plugins/blob/master/CONTRIBUTING.md
|
||||
- [ ] I opened an issue first for non-trivial changes and linked it below.
|
||||
- [ ] AI tools were used to create at least part of the code submitted herewith.
|
||||
|
||||
If AI was used, please disclose:
|
||||
|
||||
- Model used:
|
||||
- Extent of AI involvement:
|
||||
|
||||
---
|
||||
|
||||
**Related issue**
|
||||
If this pull request relates to an issue, link it here:
|
||||
|
||||
---
|
||||
|
||||
**Describe the problem**
|
||||
A clear and concise description of the problem this pull request addresses.
|
||||
|
||||
---
|
||||
|
||||
**Describe the proposed solution**
|
||||
Explain what this pull request changes and why.
|
||||
|
||||
---
|
||||
|
|
@ -47,3 +47,16 @@ When creating pull request, please heed the following:
|
|||
* Code review may ensue in order to help shape your proposal
|
||||
* Pull request must adhere to 2-Clause BSD licensing
|
||||
* Explain the problem and your proposed solution
|
||||
|
||||
New plugins
|
||||
-----------
|
||||
|
||||
The pull request notes apply, but with the following additional points:
|
||||
|
||||
* Open an issue first to explain what you want to work on and give it time for discussion
|
||||
* If you are integrating a service binary it should at least be available in FreeBSD ports
|
||||
* Precompiled binaries in the plugins are not allowed
|
||||
* Plugins should almost always focus on integrating an existing service and providing MVC/API GUI pages for it
|
||||
* It is not possible to review and integrate plugins with a large initial codebase
|
||||
* If you use AI tools in your submission please disclose their use (name and model)
|
||||
* Even though you are the maintainer you effectively force burden of maintainership to the community and OPNsense developers as soon as you open your first PR
|
||||
|
|
|
|||
4
LICENSE
4
LICENSE
|
|
@ -12,7 +12,7 @@ Copyright (c) 2021 Axelrtgs
|
|||
Copyright (c) 2026 Benno Kutschenreuter
|
||||
Copyright (c) 2023 Bernhard Frenking <bernhard@frenking.eu>
|
||||
Copyright (c) 2023 Cannon Matthews <cannonmatthews@google.com>
|
||||
Copyright (c) 2023-2025 Cedrik Pischem
|
||||
Copyright (c) 2023-2026 Cedrik Pischem
|
||||
Copyright (c) 2025 Christopher Linn, BackendMedia IT-Services GmbH
|
||||
Copyright (c) 2005-2006 Colin Smith <ethethlay@gmail.com>
|
||||
Copyright (c) 2021 Dan Lundqvist
|
||||
|
|
@ -31,7 +31,7 @@ Copyright (c) 2019 Felix Matouschek <felix@matouschek.org>
|
|||
Copyright (c) 2025 Florian Latifi
|
||||
Copyright (c) 2024 Francisco Dimattia <info@tecnoservicio.com.ar>
|
||||
Copyright (c) 2014-2025 Franco Fichtner <franco@opnsense.org>
|
||||
Copyright (c) 2016-2025 Frank Wall
|
||||
Copyright (c) 2016-2026 Frank Wall
|
||||
Copyright (c) 2021 Github-jjw
|
||||
Copyright (c) 2023 Greg Glockner <greg@glockners.net>
|
||||
Copyright (c) 2024 Hasan Ucak <hasan@sunnyvalley.io>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
PLUGIN_NAME= redis
|
||||
PLUGIN_VERSION= 1.1
|
||||
PLUGIN_REVISION= 3
|
||||
PLUGIN_REVISION= 4
|
||||
PLUGIN_COMMENT= Redis DB
|
||||
PLUGIN_DEPENDS= redis72
|
||||
PLUGIN_MAINTAINER= franz.fabian.94@gmail.com
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ Plugin Changelog
|
|||
1.1
|
||||
|
||||
* Add a button to reset all databases (contributed by Michael Muenz)
|
||||
* Fix service widget behaviour (contributed by sevengiants)
|
||||
|
||||
1.0
|
||||
|
||||
|
|
@ -31,6 +32,3 @@ Plugin Changelog
|
|||
* Allow password protection
|
||||
* Connection limits
|
||||
* Performance monitoring of slow connections
|
||||
|
||||
|
||||
WWW: http://redis.io/
|
||||
|
|
|
|||
|
|
@ -34,12 +34,11 @@ $( document ).ready(function() {
|
|||
mapDataToFormUI(data_get_map).done(function(){
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
// request service status on load and update status box
|
||||
ajaxCall(url="/api/redis/service/status", sendData={}, callback=function(data,status) {
|
||||
updateServiceStatusUI(data['status']);
|
||||
});
|
||||
});
|
||||
|
||||
// request service status on load and update status box
|
||||
updateServiceControlUI('redis');
|
||||
|
||||
// update history on tab state and implement navigation
|
||||
if(window.location.hash != "") {
|
||||
$('a[href="' + window.location.hash + '"]').click()
|
||||
|
|
@ -73,9 +72,7 @@ $( document ).ready(function() {
|
|||
draggable: true
|
||||
});
|
||||
} else {
|
||||
ajaxCall(url="/api/redis/service/status", sendData={}, callback=function(data,status) {
|
||||
updateServiceStatusUI(data['status']);
|
||||
});
|
||||
updateServiceControlUI('redis');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
PLUGIN_NAME= ddclient
|
||||
PLUGIN_VERSION= 1.30
|
||||
PLUGIN_REVISION= 2
|
||||
PLUGIN_DEPENDS= ddclient py${PLUGIN_PYTHON}-boto3
|
||||
PLUGIN_COMMENT= Dynamic DNS client
|
||||
PLUGIN_MAINTAINER= ad@opnsense.org
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ Plugin Changelog
|
|||
1.30
|
||||
|
||||
* Add native backend support for Hostinger (contributed by Leandro Scardua)
|
||||
* Fix Hetzner existing record update (contributed by Julian Nikodemus)
|
||||
* Fix PowerDNS URL validation
|
||||
|
||||
1.29
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
<id>account.zone</id>
|
||||
<label>Zone</label>
|
||||
<type>text</type>
|
||||
<style>optional_setting service_aws service_zoneedit1 service_cloudflare service_nsupdate service_gandi service_godaddy service_nfsn service_hetzner service_digitalocean service_dnspodcn service_hostinger</style>
|
||||
<style>optional_setting service_aws service_zoneedit1 service_cloudflare service_nsupdate service_gandi service_godaddy service_nfsn service_hetzner service_digitalocean service_dnspodcn service_allinkl</style>
|
||||
<help>Zone containing the host entry.</help>
|
||||
</field>
|
||||
<field>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021-2023 Deciso B.V.
|
||||
* Copyright (C) 2021-2026 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -41,6 +41,7 @@ class DynDNS extends BaseModel
|
|||
{
|
||||
$messages = parent::performValidation($validateFullModel);
|
||||
$validate_servers = [];
|
||||
|
||||
foreach ($this->getFlatNodes() as $key => $node) {
|
||||
$tagName = $node->getInternalXMLTagName();
|
||||
$parentNode = $node->getParentNode();
|
||||
|
|
@ -51,27 +52,24 @@ class DynDNS extends BaseModel
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($validate_servers as $key => $node) {
|
||||
if ((string)$node->service == 'powerdns') {
|
||||
if (empty($srv) || filter_var($srv, FILTER_VALIDATE_URL) === false) {
|
||||
$messages->appendMessage(
|
||||
new Message(
|
||||
gettext("A valid URI is required."),
|
||||
$key . ".server"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if ((string)$node->service != 'custom') {
|
||||
$validate_url = false;
|
||||
|
||||
if ($node->service->isEqual('powerdns')) {
|
||||
$validate_url = true;
|
||||
} elseif (!$node->service->isEqual('custom')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$srv = (string)$node->server;
|
||||
if (in_array((string)$node->protocol, ['get', 'post', 'put'])) {
|
||||
|
||||
if (in_array((string)$node->protocol, ['get', 'post', 'put']) || $validate_url) {
|
||||
if (empty($srv) || filter_var($srv, FILTER_VALIDATE_URL) === false) {
|
||||
$messages->appendMessage(
|
||||
new Message(
|
||||
gettext("A valid URI is required."),
|
||||
$key . ".server"
|
||||
gettext('A valid URI is required.'),
|
||||
$key . '.server'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -79,13 +77,14 @@ class DynDNS extends BaseModel
|
|||
if (empty($srv) || filter_var($srv, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) {
|
||||
$messages->appendMessage(
|
||||
new Message(
|
||||
gettext("A valid domain is required."),
|
||||
$key . ".server"
|
||||
gettext('A valid domain is required.'),
|
||||
$key . '.server'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,337 @@
|
|||
"""
|
||||
Copyright (c) 2026 Carsten Kallies
|
||||
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.
|
||||
|
||||
all-inkl.com KAS API DynDNS provider for OPNsense ddclient.
|
||||
|
||||
Uses the KAS SOAP API (KasApi.wsdl) to update A/AAAA records.
|
||||
|
||||
API endpoint: https://kasapi.kasserver.com/soap/KasApi.php
|
||||
WSDL: https://kasapi.kasserver.com/soap/wsdl/KasApi.wsdl
|
||||
|
||||
UI fields:
|
||||
username - KAS login (all-inkl username, e.g. "w0xxxxx")
|
||||
password - KAS password (plaintext, transmitted over HTTPS)
|
||||
hostnames - FQDN(s) to update, comma-separated (e.g. "example.com,*.example.com")
|
||||
zone - DNS zone (e.g. "example.com"); derived from hostname if left empty
|
||||
"""
|
||||
import json
|
||||
import syslog
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import requests
|
||||
|
||||
from . import BaseAccount
|
||||
|
||||
|
||||
class AllInkl(BaseAccount):
|
||||
"""all-inkl.com DynDNS via KAS SOAP API (KasApi)."""
|
||||
|
||||
_priority = 65535
|
||||
|
||||
_services = {
|
||||
'allinkl': 'kasapi.kasserver.com'
|
||||
}
|
||||
|
||||
_URL = 'https://kasapi.kasserver.com/soap/KasApi.php'
|
||||
_ACTION = '"urn:xmethodsKasApi#KasApi"'
|
||||
|
||||
def __init__(self, account: dict):
|
||||
super().__init__(account)
|
||||
|
||||
@staticmethod
|
||||
def known_services():
|
||||
return {'allinkl': 'all-inkl.com (KAS API)'}
|
||||
|
||||
@staticmethod
|
||||
def match(account):
|
||||
return account.get('service') in AllInkl._services
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# SOAP / KAS helpers
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _build_envelope(self, params_dict):
|
||||
"""Build a KasApi SOAP envelope. params_dict is JSON-serialised into <Params>."""
|
||||
params_json = json.dumps(params_dict)
|
||||
# Escape XML special characters in the JSON string
|
||||
params_json = (params_json
|
||||
.replace('&', '&')
|
||||
.replace('<', '<')
|
||||
.replace('>', '>'))
|
||||
return (
|
||||
'<?xml version="1.0" encoding="utf-8"?>'
|
||||
'<SOAP-ENV:Envelope'
|
||||
' xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"'
|
||||
' xmlns:ns1="urn:xmethodsKasApi"'
|
||||
' xmlns:xsd="http://www.w3.org/2001/XMLSchema"'
|
||||
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
|
||||
' xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"'
|
||||
' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
|
||||
'<SOAP-ENV:Body>'
|
||||
'<ns1:KasApi>'
|
||||
'<Params xsi:type="xsd:string">' + params_json + '</Params>'
|
||||
'</ns1:KasApi>'
|
||||
'</SOAP-ENV:Body>'
|
||||
'</SOAP-ENV:Envelope>'
|
||||
)
|
||||
|
||||
def _kas_api(self, action, request_params):
|
||||
"""Execute a KAS API action. Returns response text or None on failure."""
|
||||
params = {
|
||||
'kas_login': self.settings.get('username', ''),
|
||||
'kas_auth_type': 'plain',
|
||||
'kas_auth_data': self.settings.get('password', ''),
|
||||
'kas_action': action,
|
||||
'KasRequestParams': request_params,
|
||||
}
|
||||
envelope = self._build_envelope(params)
|
||||
|
||||
if self.is_verbose:
|
||||
syslog.syslog(
|
||||
syslog.LOG_NOTICE,
|
||||
"Account %s KAS action '%s' params: %s" % (
|
||||
self.description, action, json.dumps(request_params)
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
resp = requests.post(
|
||||
self._URL,
|
||||
data=envelope.encode('utf-8'),
|
||||
headers={
|
||||
'Content-Type': 'text/xml; charset=utf-8',
|
||||
'SOAPAction': self._ACTION,
|
||||
'User-Agent': 'OPNsense-dyndns',
|
||||
},
|
||||
timeout=30
|
||||
)
|
||||
except requests.RequestException as exc:
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s KAS request failed: %s" % (self.description, exc)
|
||||
)
|
||||
return None
|
||||
|
||||
if self.is_verbose:
|
||||
syslog.syslog(
|
||||
syslog.LOG_NOTICE,
|
||||
"Account %s KAS '%s' HTTP %d: %s" % (
|
||||
self.description, action, resp.status_code, resp.text[:600]
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
root = ET.fromstring(resp.text)
|
||||
except ET.ParseError:
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s KAS '%s' invalid XML response: %s" % (
|
||||
self.description, action, resp.text[:200]
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
fault = root.find('.//{*}Fault')
|
||||
if fault is not None:
|
||||
faultstring = fault.find('{*}faultstring') or fault.find('faultstring')
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s KAS '%s' SOAP fault: %s" % (
|
||||
self.description, action,
|
||||
faultstring.text if faultstring is not None else resp.text[:200]
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
return resp.text
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Response parsing
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _find_record_id(self, xml_text, record_label, record_type):
|
||||
"""
|
||||
Parse get_dns_settings response and return record_id for the matching
|
||||
record_name / record_type, or None.
|
||||
|
||||
The KAS response contains ns2:Map items with key/value pairs:
|
||||
<item><key ...>record_name</key><value ...>dyn</value></item>
|
||||
<item><key ...>record_type</key><value ...>A</value></item>
|
||||
<item><key ...>record_id</key><value ...>12345</value></item>
|
||||
"""
|
||||
root = ET.fromstring(xml_text)
|
||||
|
||||
for map_item in root.findall('.//{*}KasApiResponse//{*}item'):
|
||||
kv = {}
|
||||
for sub in map_item.findall('{*}item'):
|
||||
key_el = sub.find('{*}key')
|
||||
value_el = sub.find('{*}value')
|
||||
if key_el is not None and value_el is not None:
|
||||
kv[key_el.text or ''] = value_el.text or ''
|
||||
|
||||
if kv.get('record_name') == record_label and kv.get('record_type') == record_type:
|
||||
return kv.get('record_id')
|
||||
|
||||
return None
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Zone / label helpers
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _get_zone(self, hostname):
|
||||
"""Return the DNS zone for a hostname (from config or derived)."""
|
||||
zone = self.settings.get('zone', '').strip().rstrip('.')
|
||||
if zone:
|
||||
return zone
|
||||
parts = hostname.split('.')
|
||||
if len(parts) > 2:
|
||||
return '.'.join(parts[1:])
|
||||
return hostname
|
||||
|
||||
def _get_label(self, hostname, zone):
|
||||
"""Return the record label (left of zone) for a hostname.
|
||||
|
||||
Examples:
|
||||
dyn.example.com / zone example.com → 'dyn'
|
||||
*.example.com / zone example.com → '*'
|
||||
example.com / zone example.com → '' (root record)
|
||||
"""
|
||||
if hostname == zone:
|
||||
return ''
|
||||
if hostname.endswith('.' + zone):
|
||||
return hostname[:-len(zone) - 1]
|
||||
return hostname.split('.')[0]
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Main entry point
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def execute(self):
|
||||
if not super().execute():
|
||||
return False
|
||||
|
||||
record_type = "AAAA" if ':' in str(self.current_address) else "A"
|
||||
|
||||
hostnames_raw = self.settings.get('hostnames', '')
|
||||
hostnames = [h.strip() for h in hostnames_raw.split(',') if h.strip()]
|
||||
if not hostnames:
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s no hostnames configured" % self.description
|
||||
)
|
||||
return False
|
||||
|
||||
all_success = True
|
||||
last_zone = None
|
||||
dns_response = None
|
||||
|
||||
for hostname in hostnames:
|
||||
zone = self._get_zone(hostname)
|
||||
zone_host = zone + '.'
|
||||
label = self._get_label(hostname, zone)
|
||||
|
||||
if self.is_verbose:
|
||||
syslog.syslog(
|
||||
syslog.LOG_NOTICE,
|
||||
"Account %s updating %s (zone: %s, label: '%s', type: %s) → %s" % (
|
||||
self.description, hostname, zone_host,
|
||||
label, record_type, self.current_address
|
||||
)
|
||||
)
|
||||
|
||||
# Fetch DNS records once per zone (cache for multiple hostnames in same zone)
|
||||
if zone != last_zone:
|
||||
dns_response = self._kas_api('get_dns_settings', {'zone_host': zone_host})
|
||||
last_zone = zone
|
||||
if dns_response is None:
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s failed to retrieve DNS settings for %s" % (
|
||||
self.description, zone_host
|
||||
)
|
||||
)
|
||||
all_success = False
|
||||
continue
|
||||
# Respect KasFloodDelay between consecutive API calls
|
||||
time.sleep(2)
|
||||
|
||||
record_id = self._find_record_id(dns_response, label, record_type)
|
||||
if record_id is None:
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s record '%s' type %s not found in zone %s" % (
|
||||
self.description, label, record_type, zone_host
|
||||
)
|
||||
)
|
||||
all_success = False
|
||||
continue
|
||||
|
||||
if self.is_verbose:
|
||||
syslog.syslog(
|
||||
syslog.LOG_NOTICE,
|
||||
"Account %s found record_id %s for label '%s' %s" % (
|
||||
self.description, record_id, label, record_type
|
||||
)
|
||||
)
|
||||
|
||||
update_response = self._kas_api('update_dns_settings', {
|
||||
'zone_host': zone_host,
|
||||
'record_id': record_id,
|
||||
'record_name': label,
|
||||
'record_type': record_type,
|
||||
'record_data': str(self.current_address),
|
||||
'record_aux': '0',
|
||||
})
|
||||
|
||||
if update_response is None:
|
||||
all_success = False
|
||||
continue
|
||||
|
||||
update_root = ET.fromstring(update_response)
|
||||
if any(v.text and v.text.upper() == 'TRUE'
|
||||
for v in update_root.findall('.//{*}value')):
|
||||
syslog.syslog(
|
||||
syslog.LOG_NOTICE,
|
||||
"Account %s set new IP %s for %s" % (
|
||||
self.description, self.current_address, hostname
|
||||
)
|
||||
)
|
||||
else:
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s update_dns_settings failed for %s: %s" % (
|
||||
self.description, hostname, update_response[:300]
|
||||
)
|
||||
)
|
||||
all_success = False
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
if all_success:
|
||||
self.update_state(address=self.current_address)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
@ -34,7 +34,23 @@ import requests
|
|||
from . import BaseAccount
|
||||
|
||||
|
||||
class Hetzner(BaseAccount):
|
||||
class HetznerAccount(BaseAccount):
|
||||
|
||||
def _extract_record_name(self, hostname, zone_name):
|
||||
"""Extract record name from hostname, handling FQDN format"""
|
||||
hostname = hostname.rstrip('.')
|
||||
if hostname.endswith('.' + zone_name):
|
||||
record_name = hostname[:-len(zone_name) - 1]
|
||||
elif hostname == zone_name:
|
||||
record_name = '@'
|
||||
else:
|
||||
record_name = hostname
|
||||
if not record_name or record_name == '@':
|
||||
record_name = '@'
|
||||
return record_name
|
||||
|
||||
|
||||
class Hetzner(HetznerAccount):
|
||||
"""
|
||||
Hetzner Cloud DNS API provider
|
||||
Uses the new Cloud API (api.hetzner.cloud)
|
||||
|
|
@ -115,52 +131,44 @@ class Hetzner(BaseAccount):
|
|||
|
||||
return zone_id
|
||||
|
||||
def _get_record(self, headers, zone_id, record_name, record_type):
|
||||
"""Get existing record by name and type"""
|
||||
def _delete_record(self, headers, zone_id, record_name, record_type):
|
||||
"""Delete existing record"""
|
||||
url = f"{self._api_base}/zones/{zone_id}/rrsets/{record_name}/{record_type}"
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
|
||||
if response.status_code == 404:
|
||||
return None
|
||||
|
||||
if response.status_code != 200:
|
||||
response = requests.delete(url, headers=headers)
|
||||
if response.status_code not in [200, 201, 204]:
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s error fetching record: HTTP %d - %s" % (
|
||||
"Account %s error deleting record for update: HTTP %d - %s" % (
|
||||
self.description, response.status_code, response.text
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
payload = response.json()
|
||||
return payload.get('rrset')
|
||||
except requests.exceptions.JSONDecodeError:
|
||||
return False
|
||||
if self.is_verbose:
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s error parsing JSON response: %s" % (self.description, response.text)
|
||||
syslog.LOG_NOTICE,
|
||||
"Account %s deleted record: %s type: %s" % (
|
||||
self.description, record_name, record_type
|
||||
)
|
||||
)
|
||||
return None
|
||||
return True
|
||||
|
||||
def _update_record(self, headers, zone_id, record_name, record_type, address):
|
||||
"""Update existing record with new address"""
|
||||
url = f"{self._api_base}/zones/{zone_id}/rrsets/{record_name}/{record_type}"
|
||||
|
||||
url = f"{self._api_base}/zones/{zone_id}/rrsets/{record_name}/{record_type}/actions/set_records"
|
||||
data = {
|
||||
'records': [{'value': str(address)}],
|
||||
'ttl': int(self.settings.get('ttl', 300))
|
||||
'records': [{
|
||||
'value': str(address)
|
||||
}]
|
||||
}
|
||||
|
||||
response = requests.put(url, headers=headers, json=data)
|
||||
|
||||
if response.status_code != 200:
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
if response.status_code not in [200, 201]:
|
||||
syslog.syslog(
|
||||
syslog.LOG_ERR,
|
||||
"Account %s error updating record: HTTP %d - %s" % (
|
||||
self.description, response.status_code, response.text
|
||||
)
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
if self.is_verbose:
|
||||
|
|
@ -205,22 +213,6 @@ class Hetzner(BaseAccount):
|
|||
|
||||
return True
|
||||
|
||||
def _extract_record_name(self, hostname, zone_name):
|
||||
"""Extract record name from hostname, handling FQDN format"""
|
||||
hostname = hostname.rstrip('.')
|
||||
|
||||
if hostname.endswith('.' + zone_name):
|
||||
record_name = hostname[:-len(zone_name) - 1]
|
||||
elif hostname == zone_name:
|
||||
record_name = '@'
|
||||
else:
|
||||
record_name = hostname
|
||||
|
||||
if not record_name or record_name == '@':
|
||||
record_name = '@'
|
||||
|
||||
return record_name
|
||||
|
||||
def execute(self):
|
||||
if super().execute():
|
||||
record_type = "AAAA" if ':' in str(self.current_address) else "A"
|
||||
|
|
@ -253,14 +245,10 @@ class Hetzner(BaseAccount):
|
|||
self.description, hostname, record_name, record_type, self.current_address
|
||||
)
|
||||
)
|
||||
|
||||
existing = self._get_record(headers, zone_id, record_name, record_type)
|
||||
|
||||
if existing:
|
||||
success = self._update_record(
|
||||
headers, zone_id, record_name, record_type, self.current_address
|
||||
)
|
||||
else:
|
||||
success = self._update_record(
|
||||
headers, zone_id, record_name, record_type, self.current_address
|
||||
)
|
||||
if not success:
|
||||
success = self._create_record(
|
||||
headers, zone_id, record_name, record_type, self.current_address
|
||||
)
|
||||
|
|
@ -282,7 +270,7 @@ class Hetzner(BaseAccount):
|
|||
return False
|
||||
|
||||
|
||||
class HetznerLegacy(BaseAccount):
|
||||
class HetznerLegacy(HetznerAccount):
|
||||
"""
|
||||
Hetzner DNS Console (Legacy) API provider
|
||||
Uses the old API at dns.hetzner.com - will be shut down May 2026
|
||||
|
|
@ -468,22 +456,6 @@ class HetznerLegacy(BaseAccount):
|
|||
|
||||
return True
|
||||
|
||||
def _extract_record_name(self, hostname, zone_name):
|
||||
"""Extract record name from hostname, handling FQDN format"""
|
||||
hostname = hostname.rstrip('.')
|
||||
|
||||
if hostname.endswith('.' + zone_name):
|
||||
record_name = hostname[:-len(zone_name) - 1]
|
||||
elif hostname == zone_name:
|
||||
record_name = '@'
|
||||
else:
|
||||
record_name = hostname
|
||||
|
||||
if not record_name or record_name == '@':
|
||||
record_name = '@'
|
||||
|
||||
return record_name
|
||||
|
||||
def execute(self):
|
||||
if super().execute():
|
||||
record_type = "AAAA" if ':' in str(self.current_address) else "A"
|
||||
|
|
@ -542,4 +514,4 @@ class HetznerLegacy(BaseAccount):
|
|||
self.update_state(address=self.current_address)
|
||||
return True
|
||||
|
||||
return False
|
||||
return False
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
PLUGIN_NAME= dnscrypt-proxy
|
||||
PLUGIN_VERSION= 1.16
|
||||
PLUGIN_REVISION= 1
|
||||
PLUGIN_REVISION= 2
|
||||
PLUGIN_COMMENT= Flexible DNS proxy supporting DNSCrypt and DoH
|
||||
PLUGIN_DEPENDS= dnscrypt-proxy2
|
||||
PLUGIN_MAINTAINER= m.muenz@gmail.com
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ Plugin Changelog
|
|||
1.16
|
||||
|
||||
* Fix ODoH servers not working (contributed by Pascal Herget)
|
||||
* Fix bootstrap_resolvers with multiple comma-separated servers (contributed by Andrei Hodorog)
|
||||
|
||||
1.15
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ tls_disable_session_tickets = true
|
|||
tls_disable_session_tickets = false
|
||||
{% endif %}
|
||||
|
||||
bootstrap_resolvers = ['{{ OPNsense.dnscryptproxy.general.fallback_resolver }}']
|
||||
bootstrap_resolvers = ['{{ OPNsense.dnscryptproxy.general.fallback_resolver.split(',') | join("','") }}']
|
||||
|
||||
{% if helpers.exists('OPNsense.dnscryptproxy.general.ignore_system_dns') and OPNsense.dnscryptproxy.general.ignore_system_dns == '1' %}
|
||||
ignore_system_dns = true
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
PLUGIN_NAME= theme-cicada
|
||||
PLUGIN_VERSION= 1.40
|
||||
PLUGIN_VERSION= 1.41
|
||||
PLUGIN_REVISION= 1
|
||||
PLUGIN_COMMENT= The cicada theme - dark grey onyx
|
||||
PLUGIN_MAINTAINER= rene@team-rebellion.net
|
||||
PLUGIN_NO_ABI= yes
|
||||
|
|
|
|||
|
|
@ -5370,7 +5370,7 @@ tbody.collapse.in {
|
|||
height: 1px;
|
||||
margin: 9px 0;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5;
|
||||
background-color: #191919;
|
||||
}
|
||||
|
||||
> {
|
||||
|
|
@ -10631,6 +10631,11 @@ ul.jqtree-tree {
|
|||
border: 1px solid #191919 !important;
|
||||
}
|
||||
|
||||
.rule.text-muted {
|
||||
background-color: #242424 !important;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.rule.text-muted > td {
|
||||
&:nth-child(1n+3) {
|
||||
text-decoration: line-through;
|
||||
|
|
|
|||
|
|
@ -3198,7 +3198,7 @@ tbody.collapse.in {
|
|||
height: 1px;
|
||||
margin: 9px 0;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5; }
|
||||
background-color: #191919; }
|
||||
.dropdown-menu > li > a {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
|
|
@ -6617,6 +6617,11 @@ ul.jqtree-tree .jqtree-title {
|
|||
border: 1px solid #191919 !important;
|
||||
}
|
||||
|
||||
.rule.text-muted {
|
||||
background-color: #242424 !important;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.rule.text-muted > td:nth-child(1n+3) {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
}
|
||||
|
||||
.tabulator-row.tabulator-row-odd.tabulator-selectable:hover:not(.tabulator-selected) {
|
||||
background-color: #242424;
|
||||
background-color: #282828;
|
||||
}
|
||||
|
||||
.tabulator-row.tabulator-row-even {
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
}
|
||||
|
||||
.tabulator-row.tabulator-row-even.tabulator-selectable:hover:not(.tabulator-selected) {
|
||||
background-color: #242424;
|
||||
background-color: #282828;
|
||||
}
|
||||
|
||||
.tabulator .tabulator-tableholder {
|
||||
|
|
@ -210,11 +210,6 @@
|
|||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.bootgrid-footer-commands {
|
||||
width: 90px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.tabulator-tableholder::after {
|
||||
content: "";
|
||||
display: block;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_NAME= theme-flexcolor
|
||||
PLUGIN_VERSION= 1.0
|
||||
PLUGIN_VERSION= 1.1
|
||||
PLUGIN_COMMENT= Theme with 3 different color schemes: black as default, light and dark-light
|
||||
PLUGIN_MAINTAINER= iengels@web.de
|
||||
PLUGIN_NO_ABI= yes
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
--stdborderprimary: #336CDF; /* standard border with accent*/
|
||||
--stdborderinverse: #000000; /* standard border with accent*/
|
||||
--stdborder50bright: #A1A1A1; /* standard border with accent*/
|
||||
--badgeback: #E6E6E6; /* badge background & progress-bar & blockquote*/
|
||||
--badgeback: #1F62C1; /* badge background & progress-bar & blockquote*/
|
||||
--progressbar: #D4D4D4; /* progress-bar*/
|
||||
--token: #AF3604; /* background token */
|
||||
--highlighted: #FFFFFF; /* highlighted element */
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
--txtboxbackhover: #000000;
|
||||
--txtboxbackactive: #000000;
|
||||
--txtboxbackdisabled: #000000;
|
||||
--txtboxbacktoken: #E65C00;
|
||||
--txtboxbacktoken: #1F62C1;
|
||||
--txtboxbackdel: #FF5252; /* only tokenize, pending delete */
|
||||
--txtboxbackdismiss: #F2F7FD; /* only tokenize, dismiss */
|
||||
/* border */
|
||||
|
|
@ -87,6 +87,8 @@
|
|||
/* special characters */
|
||||
--link: #FA6121; /* OPNsense text login and links */
|
||||
--linkhover: #E04605; /* OPNsense text login and links hover */
|
||||
--colorcheckbox: #388E3C; /* background color checkbox */
|
||||
--colorradio: #FF3333; /* background color radio button */
|
||||
/* accents */
|
||||
--primary: #336CDF; /* primary accent */
|
||||
--primaryhover: #608DE6; /* primary accent hover */
|
||||
|
|
@ -97,7 +99,27 @@
|
|||
--warning: #D66E12; /* warning accent */
|
||||
--warninghover: #ED862B; /* warning accent hover */
|
||||
--danger: #FF5252; /* danger accent */
|
||||
--dangerhover: #BE2326FF8585; /* danger accent hover */
|
||||
--dangerhover: #E64949; /* danger accent hover */
|
||||
/* alert messages */
|
||||
/* alert*/
|
||||
--alertback: #000000;
|
||||
--alertborder: transparent;
|
||||
/* alert success */
|
||||
--alertsuccessfore: #E6E6E6;
|
||||
--alertsuccessback: #000000;
|
||||
--alertsuccessborder: #36D93E;
|
||||
/* alert info */
|
||||
--alertinfofore: #E6E6E6;
|
||||
--alertinfoback: #000000;
|
||||
--alertinfoborder: #369DD9;
|
||||
/* alert warning */
|
||||
--alertwarningfore: #E6E6E6;
|
||||
--alertwarningback: #000000;
|
||||
--alertwarningborder: #D98236;
|
||||
/* alert danger */
|
||||
--alertdangerfore: #E6E6E6;
|
||||
--alertdangerback: #000000;
|
||||
--alertdangerborder: #D93636;
|
||||
/* buttons (complete (independent) */
|
||||
/* unselected */
|
||||
--btnfore: #E6E6E6; /* textcolor */
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
--stdborderprimary: #FA6121; /* standard border with accent*/
|
||||
--stdborderinverse: #647D9B; /* standard border with accent*/
|
||||
--stdborder50bright: #A2B1C3; /* standard border with accent*/
|
||||
--badgeback: #FFC59E; /* badge background & progress-bar & blockquote*/
|
||||
--badgeback: #AD7A7A; /* badge background & progress-bar & blockquote*/
|
||||
--progressbar: #181E25; /* progress-bar*/
|
||||
--token: #FA6121; /* background token */
|
||||
--highlighted: #FFFFFF; /* highlighted element */
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
--txtboxbackhover: #F2F7FD;
|
||||
--txtboxbackactive: #F2F7FD;
|
||||
--txtboxbackdisabled: #181E25;
|
||||
--txtboxbacktoken: #FFC59E;
|
||||
--txtboxbacktoken: #AD7A7A;
|
||||
--txtboxbackdel: #DB393D; /* only tokenize, pending delete */
|
||||
--txtboxbackdismiss: #F2F7FD; /* only tokenize, dismiss */
|
||||
/* border */
|
||||
|
|
@ -87,6 +87,8 @@
|
|||
/* special characters */
|
||||
--link: #FA6121; /* OPNsense text login and links */
|
||||
--linkhover: #E04605; /* OPNsense text login and links hover */
|
||||
--colorcheckbox: #39E63C; /* background color checkbox */
|
||||
--colorradio: #0089D0; /* background color radio button */
|
||||
/* accents */
|
||||
--primary: #FA6121; /* primary accent */
|
||||
--primaryhover: #E04605; /* primary accent hover */
|
||||
|
|
@ -98,6 +100,26 @@
|
|||
--warninghover: #B87A00; /* warning accent hover */
|
||||
--danger: #DB393D; /* danger accent */
|
||||
--dangerhover: #BE2326; /* danger accent hover */
|
||||
/* alert messages */
|
||||
/* alert*/
|
||||
--alertback: #181E25;
|
||||
--alertborder: transparent;
|
||||
/* alert success */
|
||||
--alertsuccessfore: #00E604;
|
||||
--alertsuccessback: #181E25;
|
||||
--alertsuccessborder: #F2F7FD;
|
||||
/* alert info */
|
||||
--alertinfofore: #0099E6;
|
||||
--alertinfoback: #181E25;
|
||||
--alertinfoborder: #F2F7FD;
|
||||
/* alert warning */
|
||||
--alertwarningfore: #E69900;
|
||||
--alertwarningback: #181E25;
|
||||
--alertwarningborder: #F2F7FD;
|
||||
/* alert danger */
|
||||
--alertdangerfore: #E60004;
|
||||
--alertdangerback: #181E25;
|
||||
--alertdangerborder: #F2F7FD;
|
||||
/* buttons (complete (independent) */
|
||||
/* unselected */
|
||||
--btnfore: #181E25; /* textcolor */
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
--stdborderprimary: #336CDF; /* standard border with accent*/
|
||||
--stdborderinverse: #131313; /* standard border with accent*/
|
||||
--stdborder50bright: #A1A1A1; /* standard border with accent*/
|
||||
--badgeback: #FFC59E; /* badge background & progress-bar & blockquote*/
|
||||
--badgeback: #8DA6BD; /* badge background & progress-bar & blockquote*/
|
||||
--progressbar: #131313; /* progress-bar*/
|
||||
--token: #71ADF4; /* background token */
|
||||
--highlighted: #131313; /* highlighted element */
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
--txtboxbackhover: #F5F5F5;
|
||||
--txtboxbackactive: #F5F5F5;
|
||||
--txtboxbackdisabled: #F5F5F5;
|
||||
--txtboxbacktoken: #71ADF4;
|
||||
--txtboxbacktoken: #8DA6BD;
|
||||
--txtboxbackdel: #FC2529; /* only tokenize, pending delete */
|
||||
--txtboxbackdismiss: #F5F5F5; /* only tokenize, dismiss */
|
||||
/* border */
|
||||
|
|
@ -87,6 +87,8 @@
|
|||
/* special characters */
|
||||
--link: #FA6121; /* OPNsense text login and links */
|
||||
--linkhover: #E04605; /* OPNsense text login and links hover */
|
||||
--colorcheckbox: #28A839; /* background color checkbox */
|
||||
--colorradio: #1066CC; /* background color radio button */
|
||||
/* accents */
|
||||
--primary: #1066CC; /* primary accent */
|
||||
--primaryhover: #0C4E9C; /* primary accent hover */
|
||||
|
|
@ -98,6 +100,26 @@
|
|||
--warninghover: #D97B03; /* warning accent hover */
|
||||
--danger: #FC2529; /* danger accent */
|
||||
--dangerhover: #EC0308; /* danger accent hover */
|
||||
/* alert messages */
|
||||
/* alert*/
|
||||
--alertback: #F5F5F5;
|
||||
--alertborder: transparent;
|
||||
/* alert success */
|
||||
--alertsuccessfore: #000000;
|
||||
--alertsuccessback: #D4FAD9;
|
||||
--alertsuccessborder: #28A839;
|
||||
/* alert info */
|
||||
--alertinfofore: #000000;
|
||||
--alertinfoback: #D4F1FA;
|
||||
--alertinfoborder: #10A6D8;
|
||||
/* alert warning */
|
||||
--alertwarningfore: #000000;
|
||||
--alertwarningback: #F2E3CE;
|
||||
--alertwarningborder: #FC9510;
|
||||
/* alert danger */
|
||||
--alertdangerfore: #000000;
|
||||
--alertdangerback: #F2CECE;
|
||||
--alertdangerborder: #FC2529;
|
||||
/* buttons (complete (independent) */
|
||||
/* unselected */
|
||||
--btnfore: #454545; /* textcolor */
|
||||
|
|
|
|||
|
|
@ -119,10 +119,14 @@ input::-moz-focus-inner {
|
|||
padding: 0; }
|
||||
input {
|
||||
line-height: normal; }
|
||||
input[type=checkbox],
|
||||
input[type=checkbox] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
accent-color: var(--colorcheckbox); }
|
||||
input[type=radio] {
|
||||
box-sizing: border-box;
|
||||
padding: 0; }
|
||||
padding: 0;
|
||||
accent-color: var(--colorradio); }
|
||||
input[type=number]::-webkit-inner-spin-button,
|
||||
input[type=number]::-webkit-outer-spin-button {
|
||||
height: auto; }
|
||||
|
|
@ -3996,14 +4000,11 @@ a.thumbnail:focus,
|
|||
a.thumbnail.active {
|
||||
border-color: var(--primary); }
|
||||
.alert {
|
||||
background-color: var(--pback);
|
||||
color: var(--pfore);
|
||||
padding: 15px;
|
||||
background-color: var(--alertback);
|
||||
margin-bottom: 20px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 3px;
|
||||
-webkit-box-shadow: inset 0 1px 0 var(--boxshadow), 0 1px 0 var(--boxshadow);
|
||||
box-shadow: inset 0 1px 0 var(--boxshadow), 0 1px 0 var(--boxshadow); }
|
||||
border: 2px solid var(--alertborder);
|
||||
border-radius: 3px; }
|
||||
.alert h4 {
|
||||
margin-top: 0;
|
||||
color: inherit; }
|
||||
|
|
@ -4024,25 +4025,33 @@ a.thumbnail.active {
|
|||
right: -21px;
|
||||
color: inherit; }
|
||||
.alert-success {
|
||||
border-color: var(--success); }
|
||||
color: var(--alertsuccessfore);
|
||||
background-color: var(--alertsuccessback);
|
||||
border-color: var(--alertsuccessborder); }
|
||||
.alert-success hr {
|
||||
border-top-color: var(--pfore); }
|
||||
.alert-success .alert-link {
|
||||
color: var(--primary); }
|
||||
.alert-info {
|
||||
border-color: var(--info); }
|
||||
color: var(--alertinfofore);
|
||||
background-color: var(--alertinfoback);
|
||||
border-color: var(--alertinfoborder); }
|
||||
.alert-info hr {
|
||||
border-top-color: var(--pfore); }
|
||||
.alert-info .alert-link {
|
||||
color: var(--info); }
|
||||
.alert-warning {
|
||||
border-color: var(--warning); }
|
||||
color: var(--alertwarningfore);
|
||||
background-color: var(--alertwarningback);
|
||||
border-color: var(--alertwarningborder); }
|
||||
.alert-warning hr {
|
||||
border-top-color: var(--pfore); }
|
||||
.alert-warning .alert-link {
|
||||
color: var(--primary); }
|
||||
.alert-danger {
|
||||
border-color: var(--danger); }
|
||||
color: var(--alertdangerfore);
|
||||
background-color: var(--alertdangerback);
|
||||
border-color: var(--alertdangerborder); }
|
||||
.alert-danger hr {
|
||||
border-top-color: var(--pfore); }
|
||||
.alert-danger .alert-link {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_NAME= theme-tukan
|
||||
PLUGIN_VERSION= 1.30
|
||||
PLUGIN_VERSION= 1.31
|
||||
PLUGIN_COMMENT= The tukan theme - blue/white
|
||||
PLUGIN_MAINTAINER= rene@team-rebellion.net
|
||||
PLUGIN_NO_ABI= yes
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
.tabulator-row {
|
||||
background-color: transparent;
|
||||
color: #bac3ca;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.tabulator-row.tabulator-row-odd {
|
||||
|
|
@ -210,11 +210,6 @@
|
|||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.bootgrid-footer-commands {
|
||||
width: 90px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.tabulator-tableholder::after {
|
||||
content: "";
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_NAME= theme-vicuna
|
||||
PLUGIN_VERSION= 1.50
|
||||
PLUGIN_VERSION= 1.51
|
||||
PLUGIN_COMMENT= The vicuna theme - blue sapphire
|
||||
PLUGIN_MAINTAINER= rene@team-rebellion.net
|
||||
PLUGIN_NO_ABI= yes
|
||||
|
|
|
|||
|
|
@ -5481,7 +5481,7 @@ tbody.collapse.in {
|
|||
height: 1px;
|
||||
margin: 9px 0;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5;
|
||||
background-color: #191919;
|
||||
}
|
||||
|
||||
> {
|
||||
|
|
@ -5559,7 +5559,7 @@ tbody.collapse.in {
|
|||
padding: 3px 20px;
|
||||
font-size: 12px;
|
||||
line-height: 1.428571429;
|
||||
color: #191919;
|
||||
color: #999;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3184,7 +3184,7 @@ tbody.collapse.in {
|
|||
height: 1px;
|
||||
margin: 9px 0;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5; }
|
||||
background-color: #191919; }
|
||||
.dropdown-menu > li > a {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
|
|
@ -3235,7 +3235,7 @@ tbody.collapse.in {
|
|||
padding: 3px 20px;
|
||||
font-size: 12px;
|
||||
line-height: 1.428571429;
|
||||
color: #191919;
|
||||
color: #999;
|
||||
white-space: nowrap; }
|
||||
|
||||
.dropdown-backdrop {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
}
|
||||
|
||||
.tabulator-row.tabulator-row-odd.tabulator-selectable:hover:not(.tabulator-selected) {
|
||||
background-color: #1b272f;
|
||||
background-color: #1f2c35;
|
||||
}
|
||||
|
||||
.tabulator-row.tabulator-row-even {
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
}
|
||||
|
||||
.tabulator-row.tabulator-row-even.tabulator-selectable:hover:not(.tabulator-selected) {
|
||||
background-color: #1b272f;
|
||||
background-color: #1f2c35;
|
||||
}
|
||||
|
||||
.tabulator .tabulator-tableholder {
|
||||
|
|
@ -210,11 +210,6 @@
|
|||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.bootgrid-footer-commands {
|
||||
width: 90px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.tabulator-tableholder::after {
|
||||
content: "";
|
||||
display: block;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -18,7 +18,7 @@ Plugin Changelog
|
|||
|
||||
1.12.13
|
||||
|
||||
* Implement memory_saving_mode formerly named enable_file_download (contributed by sopex)
|
||||
* Implement memory_saving_mode formerly named enable_file_download (contributed by Konstantinos Spartalis)
|
||||
|
||||
1.12.12
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
PLUGIN_NAME= frr
|
||||
PLUGIN_VERSION= 1.50
|
||||
PLUGIN_REVISION= 1
|
||||
PLUGIN_VERSION= 1.52
|
||||
PLUGIN_COMMENT= The FRRouting Protocol Suite
|
||||
PLUGIN_DEPENDS= frr10-pythontools
|
||||
PLUGIN_MAINTAINER= ad@opnsense.org
|
||||
|
|
|
|||
|
|
@ -12,6 +12,14 @@ WWW: https://frrouting.org/
|
|||
Plugin Changelog
|
||||
================
|
||||
|
||||
1.52
|
||||
|
||||
* Add BGP maximum-paths support for ECMP multipath (contributed by maxfield-allison) (opnsense/plugins#4878)
|
||||
|
||||
1.51
|
||||
|
||||
* Add per-neighbor local-as option for BGP (contributed by danohn) (opnsense/plugins/pull/5308)
|
||||
|
||||
1.50
|
||||
|
||||
* Add BGP remove-private-AS (contributed by rfrederick) (opnsense/plugins/pull/5090)
|
||||
|
|
|
|||
|
|
@ -67,4 +67,18 @@
|
|||
<type>checkbox</type>
|
||||
<help>Enable extended logging of BGP neighbor changes.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>bgp.maximumpaths</id>
|
||||
<label>Maximum Paths</label>
|
||||
<type>text</type>
|
||||
<advanced>true</advanced>
|
||||
<help>Maximum number of equal-cost paths for EBGP multipath (ECMP). Leave empty to use FRR default (1).</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>bgp.maximumpathsibgp</id>
|
||||
<label>Maximum Paths (IBGP)</label>
|
||||
<type>text</type>
|
||||
<advanced>true</advanced>
|
||||
<help>Maximum number of equal-cost paths for IBGP multipath (ECMP). Leave empty to use FRR default (1).</help>
|
||||
</field>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@
|
|||
<type>text</type>
|
||||
<help>AS (Autonomous System) number of the neighbor, required for establishing a BGP session.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>neighbor.localas</id>
|
||||
<label>Local AS</label>
|
||||
<type>text</type>
|
||||
<help>Optional local AS to present to this specific neighbor.</help>
|
||||
<grid_view>
|
||||
<visible>false</visible>
|
||||
</grid_view>
|
||||
</field>
|
||||
<field>
|
||||
<id>neighbor.password</id>
|
||||
<label>BGP MD5 Password</label>
|
||||
|
|
|
|||
|
|
@ -48,6 +48,16 @@
|
|||
</OptionValues>
|
||||
<Multiple>Y</Multiple>
|
||||
</bestpath>
|
||||
<maximumpaths type="IntegerField">
|
||||
<MinimumValue>1</MinimumValue>
|
||||
<MaximumValue>128</MaximumValue>
|
||||
<ValidationMessage>The value shall be between 1 and 128 or left empty to use the default.</ValidationMessage>
|
||||
</maximumpaths>
|
||||
<maximumpathsibgp type="IntegerField">
|
||||
<MinimumValue>1</MinimumValue>
|
||||
<MaximumValue>128</MaximumValue>
|
||||
<ValidationMessage>The value shall be between 1 and 128 or left empty to use the default.</ValidationMessage>
|
||||
</maximumpathsibgp>
|
||||
<neighbors>
|
||||
<neighbor type="ArrayField">
|
||||
<enabled type="BooleanField">
|
||||
|
|
@ -69,6 +79,10 @@
|
|||
<MinimumValue>1</MinimumValue>
|
||||
<MaximumValue>4294967295</MaximumValue>
|
||||
</remoteas>
|
||||
<localas type="IntegerField">
|
||||
<MinimumValue>1</MinimumValue>
|
||||
<MaximumValue>4294967295</MaximumValue>
|
||||
</localas>
|
||||
<password type="TextField"/>
|
||||
<weight type="IntegerField">
|
||||
<MinimumValue>0</MinimumValue>
|
||||
|
|
|
|||
|
|
@ -120,6 +120,9 @@ router bgp {{ OPNsense.quagga.bgp.asnumber }}
|
|||
{% else %}
|
||||
neighbor {{ neighbor.address }} remote-as {{ neighbor.remote_as_mode }}
|
||||
{% endif %}
|
||||
{% if 'localas' in neighbor and neighbor.localas != '' %}
|
||||
neighbor {{ neighbor.address }} local-as {{ neighbor.localas }}
|
||||
{% endif %}
|
||||
{% if neighbor.bfd|default('') == '1' %}
|
||||
neighbor {{ neighbor.address }} bfd
|
||||
{% endif %}
|
||||
|
|
@ -167,6 +170,12 @@ router bgp {{ OPNsense.quagga.bgp.asnumber }}
|
|||
|
||||
{% for addressFamily in addressFamilies %}
|
||||
address-family {{ addressFamily }} unicast
|
||||
{% if helpers.exists('OPNsense.quagga.bgp.maximumpaths') and OPNsense.quagga.bgp.maximumpaths != '' %}
|
||||
maximum-paths {{ OPNsense.quagga.bgp.maximumpaths }}
|
||||
{% endif %}
|
||||
{% if helpers.exists('OPNsense.quagga.bgp.maximumpathsibgp') and OPNsense.quagga.bgp.maximumpathsibgp != '' %}
|
||||
maximum-paths ibgp {{ OPNsense.quagga.bgp.maximumpathsibgp }}
|
||||
{% endif %}
|
||||
{% for redistribution in helpers.toList('OPNsense.quagga.bgp.redistributions.redistribution') %}
|
||||
{% if redistribution.enabled == '1' %}
|
||||
redistribute {{ redistribution.redistribute }}{% if redistribution.linkedRoutemap %} route-map {{ helpers.getUUID(redistribution.linkedRoutemap).name }}{% endif +%}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{% if helpers.exists('OPNsense.quagga.general.enabled') and OPNsense.quagga.general.enabled == '1' %}
|
||||
# XXX rc.d/frr splits up defunct "frr" service into frr_daemons
|
||||
# and we always start "zebra" so for now this works:
|
||||
zebra_setup="/usr/local/opnsense/scripts/frr/setup.sh"
|
||||
watchfrr_setup="/usr/local/opnsense/scripts/frr/setup.sh"
|
||||
frr_enable="YES"
|
||||
{% if helpers.exists('OPNsense.quagga.general.enablecarp') and OPNsense.quagga.general.enablecarp == '1' %}
|
||||
start_precmd="ifconfig | grep 'carp: MASTER'"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_NAME= haproxy
|
||||
PLUGIN_VERSION= 5.0
|
||||
PLUGIN_VERSION= 5.1
|
||||
PLUGIN_COMMENT= Reliable, high performance TCP/HTTP load balancer
|
||||
PLUGIN_DEPENDS= haproxy py${PLUGIN_PYTHON}-haproxy-cli
|
||||
PLUGIN_MAINTAINER= opnsense@moov.de
|
||||
|
|
|
|||
|
|
@ -6,6 +6,18 @@ very high loads while needing persistence or Layer7 processing.
|
|||
Plugin Changelog
|
||||
================
|
||||
|
||||
5.1
|
||||
|
||||
Added:
|
||||
* more conditions have support for converters
|
||||
* add support for mapfile URLs (#4825)
|
||||
|
||||
Fixed:
|
||||
* migration fails if a http-request/-response "lua" rule is configured
|
||||
|
||||
Changed:
|
||||
* modernize UI templates
|
||||
|
||||
5.0
|
||||
|
||||
WARNING: This is a new major release, which may result in
|
||||
|
|
@ -18,7 +30,7 @@ Added:
|
|||
* add new condition: HTTP method
|
||||
* support custom HTTP status code in "http-request deny" rules
|
||||
* add new backend option to control PROXY protocol for health checks (#2909)
|
||||
* add support for new map file types: beg,end,int,ip,reg,str (#3641)
|
||||
* add support for new map file types: beg,end,int,ip,reg,str,sub (#3641)
|
||||
* add support for more sample fetches: quic_enabled, stopping, wait_end (#3702)
|
||||
* add support for HTTP compression (#4867)
|
||||
* add all action keywords for http-request/-response and tcp-request/-response rules
|
||||
|
|
@ -27,7 +39,8 @@ Added:
|
|||
* add support for GPC/GPT/SC to conditions and rules (#1123, #5109)
|
||||
* add support for SSL SNI expression to servers (#3756)
|
||||
* add column "mode" to servers overview (#4632)
|
||||
* add support for loading mapfiles in conditions
|
||||
* add support for loading mapfiles in conditions and rules
|
||||
* add support for sample fetches in rules
|
||||
|
||||
Fixed:
|
||||
* Maintenance tab "SSL Certificates" not working with only one cert
|
||||
|
|
|
|||
|
|
@ -266,6 +266,27 @@
|
|||
<type>text</type>
|
||||
<help><![CDATA[Specify the value for the URL parameter.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<label>Parameters</label>
|
||||
<type>header</type>
|
||||
<style>expression_table table_var</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>acl.var_comparison</id>
|
||||
<label>Comparison</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>acl.var</id>
|
||||
<label>Variable</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[The name of a variable, followed by an optional default value, e.g. (req.rate_limit) or (req.rate_limit,10).]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>acl.var_value</id>
|
||||
<label>Value</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<label>Parameters</label>
|
||||
<type>header</type>
|
||||
|
|
@ -1788,4 +1809,10 @@
|
|||
<type>dropdown</type>
|
||||
<help><![CDATA[Load patterns from a map file.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>acl.converter</id>
|
||||
<label>Converter</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Transforms or processes the output of a sample fetch (e.g. variable, mapping, arithmetic, string manipulation) and passes the result to the next converter or final evaluation. Make more complex conditions possible, e.g. acl rate_abuse var(req.rate_limit),sub(req.request_rate) lt 0.]]></help>
|
||||
</field>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -325,4 +325,22 @@
|
|||
<type>text</type>
|
||||
<help><![CDATA[Refers to the number of the Stick Counter, e.g. 0 or 1 when using sc0 or sc1 respectively. This value will be ignored in rules that do not support SC.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.mapfile</id>
|
||||
<label>Mapfile</label>
|
||||
<type>dropdown</type>
|
||||
<help><![CDATA[Load patterns from a map file.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.map_default</id>
|
||||
<label>Map default value</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[When a map file is specified, this is the default value.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.sample_fetch</id>
|
||||
<label>Sample fetch</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[Extracts a value from the request or connection context (e.g. path, header, source IP) to be used as input for converters or matching rules. May be used in combination with set-var and map files to create more complex rules, e.g.<br/>http-request set-var(req.rate_limit) path,map_beg(/path/to/mapfile,20)</br>http-request set-var(req.request_rate) base32+src,table_http_req_rate()]]></help>
|
||||
</field>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@
|
|||
<id>mapfile.content</id>
|
||||
<label>Content</label>
|
||||
<type>textbox</type>
|
||||
<help><![CDATA[Paste the content of your map file here. See the <a target="_blank" href="http://docs.haproxy.org/3.2/configuration.html#map">HAProxy documentation</a> for a full description.]]></help>
|
||||
<help><![CDATA[Paste the content of the map file here. See the <a target="_blank" href="http://docs.haproxy.org/3.2/configuration.html#map">HAProxy documentation</a> for a full description.]]></help>
|
||||
</field>
|
||||
<field>
|
||||
<id>mapfile.url</id>
|
||||
<label>Download URL</label>
|
||||
<type>text</type>
|
||||
<help><![CDATA[A URL from which the map file will be downloaded. The contents will be cached until next reload/restart of HAProxy. If the download fails, then the specified content will be used instead. In order to enable automatic updates of map files, setup a cron job that periodically reloads HAProxy.]]></help>
|
||||
</field>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1825,133 +1825,134 @@
|
|||
<expression type="OptionField">
|
||||
<Required>Y</Required>
|
||||
<OptionValues>
|
||||
<cust_hdr_beg>hdr_beg – specified HTTP Header starts with</cust_hdr_beg>
|
||||
<cust_hdr_end>hdr_end – specified HTTP Header ends with</cust_hdr_end>
|
||||
<cust_hdr>hdr – specified HTTP Header matches</cust_hdr>
|
||||
<cust_hdr_reg>hdr_reg – specified HTTP Header regex</cust_hdr_reg>
|
||||
<cust_hdr_sub>hdr_sub – specified HTTP Header contains</cust_hdr_sub>
|
||||
<hdr_beg>hdr_beg – HTTP Host Header starts with</hdr_beg>
|
||||
<hdr_end>hdr_end – HTTP Host Header ends with</hdr_end>
|
||||
<hdr>hdr – HTTP Host Header matches</hdr>
|
||||
<hdr_reg>hdr_reg – HTTP Host Header regex</hdr_reg>
|
||||
<hdr_sub>hdr_sub – HTTP Host Header contains</hdr_sub>
|
||||
<http_auth>http_auth – HTTP Basic Auth: username/password from client matches selected User/Group</http_auth>
|
||||
<http_method>http_method – HTTP Method</http_method>
|
||||
<nbsrv>nbsrv – Minimum number of usable servers in backend</nbsrv>
|
||||
<path_beg>path_beg – Path starts with</path_beg>
|
||||
<path_dir>path_dir – Path contains subdir</path_dir>
|
||||
<path_end>path_end – Path ends with</path_end>
|
||||
<path>path – Path matches</path>
|
||||
<path_reg>path_reg – Path regex</path_reg>
|
||||
<path_sub>path_sub – Path contains string</path_sub>
|
||||
<quic_enabled>quic_enabled – QUIC transport protocol is enabled</quic_enabled>
|
||||
<traffic_is_http>req.proto_http – Traffic is HTTP</traffic_is_http>
|
||||
<traffic_is_ssl>req.ssl_ver – Traffic is SSL (TCP request content inspection)</traffic_is_ssl>
|
||||
<sc_bytes_in_rate>sc_bytes_in_rate – Sticky counter: incoming bytes rate</sc_bytes_in_rate>
|
||||
<sc_bytes_out_rate>sc_bytes_out_rate – Sticky counter: outgoing bytes rate</sc_bytes_out_rate>
|
||||
<sc_clr_gpc>sc_clr_gpc – Sticky counter: clear General Purpose Counter</sc_clr_gpc>
|
||||
<sc_clr_gpc0>sc_clr_gpc0 – Sticky counter: clear General Purpose Counter</sc_clr_gpc0>
|
||||
<sc_clr_gpc1>sc_clr_gpc1 – Sticky counter: clear General Purpose Counter</sc_clr_gpc1>
|
||||
<sc0_clr_gpc0>sc0_clr_gpc0 – Sticky counter: clear General Purpose Counter</sc0_clr_gpc0>
|
||||
<sc0_clr_gpc1>sc0_clr_gpc1 – Sticky counter: clear General Purpose Counter</sc0_clr_gpc1>
|
||||
<sc1_clr_gpc>sc1_clr_gpc – Sticky counter: clear General Purpose Counter</sc1_clr_gpc>
|
||||
<sc1_clr_gpc0>sc1_clr_gpc0 – Sticky counter: clear General Purpose Counter</sc1_clr_gpc0>
|
||||
<sc1_clr_gpc1>sc1_clr_gpc1 – Sticky counter: clear General Purpose Counter</sc1_clr_gpc1>
|
||||
<sc2_clr_gpc>sc2_clr_gpc – Sticky counter: clear General Purpose Counter</sc2_clr_gpc>
|
||||
<sc2_clr_gpc0>sc2_clr_gpc0 – Sticky counter: clear General Purpose Counter</sc2_clr_gpc0>
|
||||
<sc2_clr_gpc1>sc2_clr_gpc1 – Sticky counter: clear General Purpose Counter</sc2_clr_gpc1>
|
||||
<sc_conn_cnt>sc_conn_cnt – Sticky counter: cumulative number of connections</sc_conn_cnt>
|
||||
<sc_conn_cur>sc_conn_cur – Sticky counter: concurrent connections</sc_conn_cur>
|
||||
<sc_conn_rate>sc_conn_rate – Sticky counter: connection rate</sc_conn_rate>
|
||||
<sc_get_gpc>sc_get_gpc – Sticky counter: get General Purpose Counter value</sc_get_gpc>
|
||||
<sc_get_gpc0>sc_get_gpc0 – Sticky counter: get General Purpose Counter value</sc_get_gpc0>
|
||||
<sc_get_gpc1>sc_get_gpc1 – Sticky counter: get General Purpose Counter value</sc_get_gpc1>
|
||||
<sc0_get_gpc0>sc0_get_gpc0 – Sticky counter: get General Purpose Counter value</sc0_get_gpc0>
|
||||
<sc0_get_gpc1>sc0_get_gpc1 – Sticky counter: get General Purpose Counter value</sc0_get_gpc1>
|
||||
<sc1_get_gpc0>sc1_get_gpc0 – Sticky counter: get General Purpose Counter value</sc1_get_gpc0>
|
||||
<sc1_get_gpc1>sc1_get_gpc1 – Sticky counter: get General Purpose Counter value</sc1_get_gpc1>
|
||||
<sc2_get_gpc0>sc2_get_gpc0 – Sticky counter: get General Purpose Counter value</sc2_get_gpc0>
|
||||
<sc2_get_gpc1>sc2_get_gpc1 – Sticky counter: get General Purpose Counter value</sc2_get_gpc1>
|
||||
<sc_get_gpt>sc_get_gpt – Sticky counter: get General Purpose Tag value</sc_get_gpt>
|
||||
<sc_get_gpt0>sc_get_gpt0 – Sticky counter: get General Purpose Tag value</sc_get_gpt0>
|
||||
<sc0_get_gpt0>sc0_get_gpt0 – Sticky counter: get General Purpose Tag value</sc0_get_gpt0>
|
||||
<sc1_get_gpt0>sc1_get_gpt0 – Sticky counter: get General Purpose Tag value</sc1_get_gpt0>
|
||||
<sc2_get_gpt0>sc2_get_gpt0 – Sticky counter: get General Purpose Tag value</sc2_get_gpt0>
|
||||
<sc_glitch_cnt>sc_glitch_cnt – Sticky counter: cumulative number of glitches</sc_glitch_cnt>
|
||||
<sc_glitch_rate>sc_glitch_rate – Sticky counter: rate of glitches</sc_glitch_rate>
|
||||
<sc_gpc_rate>sc_gpc_rate – Sticky counter: increment rate of General Purpose Counter</sc_gpc_rate>
|
||||
<sc_gpc0_rate>sc_gpc0_rate – Sticky counter: increment rate of General Purpose Counter</sc_gpc0_rate>
|
||||
<sc_gpc1_rate>sc_gpc1_rate – Sticky counter: increment rate of General Purpose Counter</sc_gpc1_rate>
|
||||
<sc0_gpc0_rate>sc0_gpc0_rate – Sticky counter: increment rate of General Purpose Counter</sc0_gpc0_rate>
|
||||
<sc0_gpc1_rate>sc0_gpc1_rate – Sticky counter: increment rate of General Purpose Counter</sc0_gpc1_rate>
|
||||
<sc1_gpc0_rate>sc1_gpc0_rate – Sticky counter: increment rate of General Purpose Counter</sc1_gpc0_rate>
|
||||
<sc1_gpc1_rate>sc1_gpc1_rate – Sticky counter: increment rate of General Purpose Counter</sc1_gpc1_rate>
|
||||
<sc2_gpc0_rate>sc2_gpc0_rate – Sticky counter: increment rate of General Purpose Counter</sc2_gpc0_rate>
|
||||
<sc2_gpc1_rate>sc2_gpc1_rate – Sticky counter: increment rate of General Purpose Counter</sc2_gpc1_rate>
|
||||
<sc_http_err_cnt>sc_http_err_cnt – Sticky counter: cumulative number of HTTP errors</sc_http_err_cnt>
|
||||
<sc_http_err_rate>sc_http_err_rate – Sticky counter: rate of HTTP errors</sc_http_err_rate>
|
||||
<sc_http_fail_cnt>sc_http_fail_cnt – Sticky counter: cumulative number of HTTP failures</sc_http_fail_cnt>
|
||||
<sc_http_fail_rate>sc_http_fail_rate – Sticky counter: rate of HTTP failures</sc_http_fail_rate>
|
||||
<sc_http_req_cnt>sc_http_req_cnt – Sticky counter: cumulative number of HTTP requests</sc_http_req_cnt>
|
||||
<sc_http_req_rate>sc_http_req_rate – Sticky counter: rate of HTTP requests</sc_http_req_rate>
|
||||
<sc_inc_gpc>sc_inc_gpc – Sticky counter: increment General Purpose Counter</sc_inc_gpc>
|
||||
<sc_inc_gpc0>sc_inc_gpc0 – Sticky counter: increment General Purpose Counter</sc_inc_gpc0>
|
||||
<sc_inc_gpc1>sc_inc_gpc1 – Sticky counter: increment General Purpose Counter</sc_inc_gpc1>
|
||||
<sc0_inc_gpc0>sc0_inc_gpc0 – Sticky counter: increment General Purpose Counter</sc0_inc_gpc0>
|
||||
<sc0_inc_gpc1>sc0_inc_gpc1 – Sticky counter: increment General Purpose Counter</sc0_inc_gpc1>
|
||||
<sc1_inc_gpc0>sc1_inc_gpc0 – Sticky counter: increment General Purpose Counter</sc1_inc_gpc0>
|
||||
<sc1_inc_gpc1>sc1_inc_gpc1 – Sticky counter: increment General Purpose Counter</sc1_inc_gpc1>
|
||||
<sc2_inc_gpc0>sc2_inc_gpc0 – Sticky counter: increment General Purpose Counter</sc2_inc_gpc0>
|
||||
<sc2_inc_gpc1>sc2_inc_gpc1 – Sticky counter: increment General Purpose Counter</sc2_inc_gpc1>
|
||||
<sc_sess_cnt>sc_sess_cnt – Sticky counter: cumulative number of sessions</sc_sess_cnt>
|
||||
<sc_sess_rate>sc_sess_rate – Sticky counter: session rate</sc_sess_rate>
|
||||
<src>src – Source IP matches specified IP</src>
|
||||
<src_bytes_in_rate>src_bytes_in_rate – Source IP: incoming bytes rate</src_bytes_in_rate>
|
||||
<src_bytes_out_rate>src_bytes_out_rate – Source IP: outgoing bytes rate</src_bytes_out_rate>
|
||||
<src_clr_gpc>src_clr_gpc – Source IP: clear General Purpose Counter</src_clr_gpc>
|
||||
<src_clr_gpc0>src_clr_gpc0 – Source IP: clear General Purpose Counter</src_clr_gpc0>
|
||||
<src_clr_gpc1>src_clr_gpc1 – Source IP: clear General Purpose Counter</src_clr_gpc1>
|
||||
<src_conn_cnt>src_conn_cnt – Source IP: cumulative number of connections</src_conn_cnt>
|
||||
<src_conn_cur>src_conn_cur – Source IP: concurrent connections</src_conn_cur>
|
||||
<src_conn_rate>src_conn_rate – Source IP: connection rate</src_conn_rate>
|
||||
<src_get_gpc>src_get_gpc – Source IP: get General Purpose Counter value</src_get_gpc>
|
||||
<src_get_gpc0>src_get_gpc0 – Source IP: get General Purpose Counter value</src_get_gpc0>
|
||||
<src_get_gpc1>src_get_gpc1 – Source IP: get General Purpose Counter value</src_get_gpc1>
|
||||
<src_get_gpt>src_get_gpt – Source IP: get General Purpose Tag value</src_get_gpt>
|
||||
<src_glitch_cnt>src_glitch_cnt – Source IP: cumulative number of glitches</src_glitch_cnt>
|
||||
<src_glitch_rate>src_glitch_rate – Source IP: rate of glitches</src_glitch_rate>
|
||||
<src_gpc_rate>src_gpc_rate – Source IP: increment rate of General Purpose Counter</src_gpc_rate>
|
||||
<src_gpc0_rate>src_gpc0_rate – Source IP: increment rate of General Purpose Counter</src_gpc0_rate>
|
||||
<src_gpc1_rate>src_gpc1_rate – Source IP: increment rate of General Purpose Counter</src_gpc1_rate>
|
||||
<src_http_err_cnt>src_http_err_cnt – Source IP: cumulative number of HTTP errors</src_http_err_cnt>
|
||||
<src_http_err_rate>src_http_err_rate – Source IP: rate of HTTP errors</src_http_err_rate>
|
||||
<src_http_fail_cnt>src_http_fail_cnt – Source IP: cumulative number of HTTP failures</src_http_fail_cnt>
|
||||
<src_http_fail_rate>src_http_fail_rate – Source IP: rate of HTTP failures</src_http_fail_rate>
|
||||
<src_http_req_cnt>src_http_req_cnt – Source IP: number of HTTP requests</src_http_req_cnt>
|
||||
<src_http_req_rate>src_http_req_rate – Source IP: rate of HTTP requests</src_http_req_rate>
|
||||
<src_inc_gpc>src_inc_gpc – Source IP: increment General Purpose Counter</src_inc_gpc>
|
||||
<src_inc_gpc0>src_inc_gpc0 – Source IP: increment General Purpose Counter</src_inc_gpc0>
|
||||
<src_inc_gpc1>src_inc_gpc1 – Source IP: increment General Purpose Counter</src_inc_gpc1>
|
||||
<src_is_local>src_is_local – Source IP is local</src_is_local>
|
||||
<src_kbytes_in>src_kbytes_in – Source IP: amount of data received (in kilobytes)</src_kbytes_in>
|
||||
<src_kbytes_out>src_kbytes_out – Source IP: amount of data sent (in kilobytes)</src_kbytes_out>
|
||||
<src_port>src_port – Source IP: TCP source port</src_port>
|
||||
<src_sess_cnt>src_sess_cnt – Source IP: cumulative number of sessions</src_sess_cnt>
|
||||
<src_sess_rate>src_sess_rate – Source IP: session rate</src_sess_rate>
|
||||
<ssl_c_ca_commonname>ssl_c_ca_commonname – SSL Client certificate issued by CA common-name</ssl_c_ca_commonname>
|
||||
<ssl_c_verify_code>ssl_c_verify_code – SSL Client certificate verify error result</ssl_c_verify_code>
|
||||
<ssl_c_verify>ssl_c_verify – SSL Client certificate is valid</ssl_c_verify>
|
||||
<ssl_fc_sni>ssl_fc_sni – SNI TLS extension matches (locally deciphered)</ssl_fc_sni>
|
||||
<ssl_fc>ssl_fc – Traffic is SSL (locally deciphered)</ssl_fc>
|
||||
<ssl_hello_type>ssl_hello_type – SSL Hello Type</ssl_hello_type>
|
||||
<ssl_sni_beg>ssl_sni_beg – SNI TLS extension starts with (TCP request content inspection)</ssl_sni_beg>
|
||||
<ssl_sni_end>ssl_sni_end – SNI TLS extension ends with (TCP request content inspection)</ssl_sni_end>
|
||||
<ssl_sni_reg>ssl_sni_reg – SNI TLS extension regex (TCP request content inspection)</ssl_sni_reg>
|
||||
<ssl_sni>ssl_sni – SNI TLS extension matches (TCP request content inspection)</ssl_sni>
|
||||
<ssl_sni_sub>ssl_sni_sub – SNI TLS extension contains (TCP request content inspection)</ssl_sni_sub>
|
||||
<stopping>stopping – HAProxy process is currently stopping</stopping>
|
||||
<url_param>url_param – URL parameter contains</url_param>
|
||||
<wait_end>wait_end – Inspection period is over</wait_end>
|
||||
<cust_hdr_beg>hdr_beg - specified HTTP Header starts with</cust_hdr_beg>
|
||||
<cust_hdr_end>hdr_end - specified HTTP Header ends with</cust_hdr_end>
|
||||
<cust_hdr>hdr - specified HTTP Header matches</cust_hdr>
|
||||
<cust_hdr_reg>hdr_reg - specified HTTP Header regex</cust_hdr_reg>
|
||||
<cust_hdr_sub>hdr_sub - specified HTTP Header contains</cust_hdr_sub>
|
||||
<hdr_beg>hdr_beg - HTTP Host Header starts with</hdr_beg>
|
||||
<hdr_end>hdr_end - HTTP Host Header ends with</hdr_end>
|
||||
<hdr>hdr - HTTP Host Header matches</hdr>
|
||||
<hdr_reg>hdr_reg - HTTP Host Header regex</hdr_reg>
|
||||
<hdr_sub>hdr_sub - HTTP Host Header contains</hdr_sub>
|
||||
<http_auth>http_auth - HTTP Basic Auth: username/password from client matches selected User/Group</http_auth>
|
||||
<http_method>http_method - HTTP Method</http_method>
|
||||
<nbsrv>nbsrv - Minimum number of usable servers in backend</nbsrv>
|
||||
<path_beg>path_beg - Path starts with</path_beg>
|
||||
<path_dir>path_dir - Path contains subdir</path_dir>
|
||||
<path_end>path_end - Path ends with</path_end>
|
||||
<path>path - Path matches</path>
|
||||
<path_reg>path_reg - Path regex</path_reg>
|
||||
<path_sub>path_sub - Path contains string</path_sub>
|
||||
<quic_enabled>quic_enabled - QUIC transport protocol is enabled</quic_enabled>
|
||||
<traffic_is_http>req.proto_http - Traffic is HTTP</traffic_is_http>
|
||||
<traffic_is_ssl>req.ssl_ver - Traffic is SSL (TCP request content inspection)</traffic_is_ssl>
|
||||
<sc_bytes_in_rate>sc_bytes_in_rate - Sticky counter: incoming bytes rate</sc_bytes_in_rate>
|
||||
<sc_bytes_out_rate>sc_bytes_out_rate - Sticky counter: outgoing bytes rate</sc_bytes_out_rate>
|
||||
<sc_clr_gpc>sc_clr_gpc - Sticky counter: clear General Purpose Counter</sc_clr_gpc>
|
||||
<sc_clr_gpc0>sc_clr_gpc0 - Sticky counter: clear General Purpose Counter</sc_clr_gpc0>
|
||||
<sc_clr_gpc1>sc_clr_gpc1 - Sticky counter: clear General Purpose Counter</sc_clr_gpc1>
|
||||
<sc0_clr_gpc0>sc0_clr_gpc0 - Sticky counter: clear General Purpose Counter</sc0_clr_gpc0>
|
||||
<sc0_clr_gpc1>sc0_clr_gpc1 - Sticky counter: clear General Purpose Counter</sc0_clr_gpc1>
|
||||
<sc1_clr_gpc>sc1_clr_gpc - Sticky counter: clear General Purpose Counter</sc1_clr_gpc>
|
||||
<sc1_clr_gpc0>sc1_clr_gpc0 - Sticky counter: clear General Purpose Counter</sc1_clr_gpc0>
|
||||
<sc1_clr_gpc1>sc1_clr_gpc1 - Sticky counter: clear General Purpose Counter</sc1_clr_gpc1>
|
||||
<sc2_clr_gpc>sc2_clr_gpc - Sticky counter: clear General Purpose Counter</sc2_clr_gpc>
|
||||
<sc2_clr_gpc0>sc2_clr_gpc0 - Sticky counter: clear General Purpose Counter</sc2_clr_gpc0>
|
||||
<sc2_clr_gpc1>sc2_clr_gpc1 - Sticky counter: clear General Purpose Counter</sc2_clr_gpc1>
|
||||
<sc_conn_cnt>sc_conn_cnt - Sticky counter: cumulative number of connections</sc_conn_cnt>
|
||||
<sc_conn_cur>sc_conn_cur - Sticky counter: concurrent connections</sc_conn_cur>
|
||||
<sc_conn_rate>sc_conn_rate - Sticky counter: connection rate</sc_conn_rate>
|
||||
<sc_get_gpc>sc_get_gpc - Sticky counter: get General Purpose Counter value</sc_get_gpc>
|
||||
<sc_get_gpc0>sc_get_gpc0 - Sticky counter: get General Purpose Counter value</sc_get_gpc0>
|
||||
<sc_get_gpc1>sc_get_gpc1 - Sticky counter: get General Purpose Counter value</sc_get_gpc1>
|
||||
<sc0_get_gpc0>sc0_get_gpc0 - Sticky counter: get General Purpose Counter value</sc0_get_gpc0>
|
||||
<sc0_get_gpc1>sc0_get_gpc1 - Sticky counter: get General Purpose Counter value</sc0_get_gpc1>
|
||||
<sc1_get_gpc0>sc1_get_gpc0 - Sticky counter: get General Purpose Counter value</sc1_get_gpc0>
|
||||
<sc1_get_gpc1>sc1_get_gpc1 - Sticky counter: get General Purpose Counter value</sc1_get_gpc1>
|
||||
<sc2_get_gpc0>sc2_get_gpc0 - Sticky counter: get General Purpose Counter value</sc2_get_gpc0>
|
||||
<sc2_get_gpc1>sc2_get_gpc1 - Sticky counter: get General Purpose Counter value</sc2_get_gpc1>
|
||||
<sc_get_gpt>sc_get_gpt - Sticky counter: get General Purpose Tag value</sc_get_gpt>
|
||||
<sc_get_gpt0>sc_get_gpt0 - Sticky counter: get General Purpose Tag value</sc_get_gpt0>
|
||||
<sc0_get_gpt0>sc0_get_gpt0 - Sticky counter: get General Purpose Tag value</sc0_get_gpt0>
|
||||
<sc1_get_gpt0>sc1_get_gpt0 - Sticky counter: get General Purpose Tag value</sc1_get_gpt0>
|
||||
<sc2_get_gpt0>sc2_get_gpt0 - Sticky counter: get General Purpose Tag value</sc2_get_gpt0>
|
||||
<sc_glitch_cnt>sc_glitch_cnt - Sticky counter: cumulative number of glitches</sc_glitch_cnt>
|
||||
<sc_glitch_rate>sc_glitch_rate - Sticky counter: rate of glitches</sc_glitch_rate>
|
||||
<sc_gpc_rate>sc_gpc_rate - Sticky counter: increment rate of General Purpose Counter</sc_gpc_rate>
|
||||
<sc_gpc0_rate>sc_gpc0_rate - Sticky counter: increment rate of General Purpose Counter</sc_gpc0_rate>
|
||||
<sc_gpc1_rate>sc_gpc1_rate - Sticky counter: increment rate of General Purpose Counter</sc_gpc1_rate>
|
||||
<sc0_gpc0_rate>sc0_gpc0_rate - Sticky counter: increment rate of General Purpose Counter</sc0_gpc0_rate>
|
||||
<sc0_gpc1_rate>sc0_gpc1_rate - Sticky counter: increment rate of General Purpose Counter</sc0_gpc1_rate>
|
||||
<sc1_gpc0_rate>sc1_gpc0_rate - Sticky counter: increment rate of General Purpose Counter</sc1_gpc0_rate>
|
||||
<sc1_gpc1_rate>sc1_gpc1_rate - Sticky counter: increment rate of General Purpose Counter</sc1_gpc1_rate>
|
||||
<sc2_gpc0_rate>sc2_gpc0_rate - Sticky counter: increment rate of General Purpose Counter</sc2_gpc0_rate>
|
||||
<sc2_gpc1_rate>sc2_gpc1_rate - Sticky counter: increment rate of General Purpose Counter</sc2_gpc1_rate>
|
||||
<sc_http_err_cnt>sc_http_err_cnt - Sticky counter: cumulative number of HTTP errors</sc_http_err_cnt>
|
||||
<sc_http_err_rate>sc_http_err_rate - Sticky counter: rate of HTTP errors</sc_http_err_rate>
|
||||
<sc_http_fail_cnt>sc_http_fail_cnt - Sticky counter: cumulative number of HTTP failures</sc_http_fail_cnt>
|
||||
<sc_http_fail_rate>sc_http_fail_rate - Sticky counter: rate of HTTP failures</sc_http_fail_rate>
|
||||
<sc_http_req_cnt>sc_http_req_cnt - Sticky counter: cumulative number of HTTP requests</sc_http_req_cnt>
|
||||
<sc_http_req_rate>sc_http_req_rate - Sticky counter: rate of HTTP requests</sc_http_req_rate>
|
||||
<sc_inc_gpc>sc_inc_gpc - Sticky counter: increment General Purpose Counter</sc_inc_gpc>
|
||||
<sc_inc_gpc0>sc_inc_gpc0 - Sticky counter: increment General Purpose Counter</sc_inc_gpc0>
|
||||
<sc_inc_gpc1>sc_inc_gpc1 - Sticky counter: increment General Purpose Counter</sc_inc_gpc1>
|
||||
<sc0_inc_gpc0>sc0_inc_gpc0 - Sticky counter: increment General Purpose Counter</sc0_inc_gpc0>
|
||||
<sc0_inc_gpc1>sc0_inc_gpc1 - Sticky counter: increment General Purpose Counter</sc0_inc_gpc1>
|
||||
<sc1_inc_gpc0>sc1_inc_gpc0 - Sticky counter: increment General Purpose Counter</sc1_inc_gpc0>
|
||||
<sc1_inc_gpc1>sc1_inc_gpc1 - Sticky counter: increment General Purpose Counter</sc1_inc_gpc1>
|
||||
<sc2_inc_gpc0>sc2_inc_gpc0 - Sticky counter: increment General Purpose Counter</sc2_inc_gpc0>
|
||||
<sc2_inc_gpc1>sc2_inc_gpc1 - Sticky counter: increment General Purpose Counter</sc2_inc_gpc1>
|
||||
<sc_sess_cnt>sc_sess_cnt - Sticky counter: cumulative number of sessions</sc_sess_cnt>
|
||||
<sc_sess_rate>sc_sess_rate - Sticky counter: session rate</sc_sess_rate>
|
||||
<src>src - Source IP matches specified IP</src>
|
||||
<src_bytes_in_rate>src_bytes_in_rate - Source IP: incoming bytes rate</src_bytes_in_rate>
|
||||
<src_bytes_out_rate>src_bytes_out_rate - Source IP: outgoing bytes rate</src_bytes_out_rate>
|
||||
<src_clr_gpc>src_clr_gpc - Source IP: clear General Purpose Counter</src_clr_gpc>
|
||||
<src_clr_gpc0>src_clr_gpc0 - Source IP: clear General Purpose Counter</src_clr_gpc0>
|
||||
<src_clr_gpc1>src_clr_gpc1 - Source IP: clear General Purpose Counter</src_clr_gpc1>
|
||||
<src_conn_cnt>src_conn_cnt - Source IP: cumulative number of connections</src_conn_cnt>
|
||||
<src_conn_cur>src_conn_cur - Source IP: concurrent connections</src_conn_cur>
|
||||
<src_conn_rate>src_conn_rate - Source IP: connection rate</src_conn_rate>
|
||||
<src_get_gpc>src_get_gpc - Source IP: get General Purpose Counter value</src_get_gpc>
|
||||
<src_get_gpc0>src_get_gpc0 - Source IP: get General Purpose Counter value</src_get_gpc0>
|
||||
<src_get_gpc1>src_get_gpc1 - Source IP: get General Purpose Counter value</src_get_gpc1>
|
||||
<src_get_gpt>src_get_gpt - Source IP: get General Purpose Tag value</src_get_gpt>
|
||||
<src_glitch_cnt>src_glitch_cnt - Source IP: cumulative number of glitches</src_glitch_cnt>
|
||||
<src_glitch_rate>src_glitch_rate - Source IP: rate of glitches</src_glitch_rate>
|
||||
<src_gpc_rate>src_gpc_rate - Source IP: increment rate of General Purpose Counter</src_gpc_rate>
|
||||
<src_gpc0_rate>src_gpc0_rate - Source IP: increment rate of General Purpose Counter</src_gpc0_rate>
|
||||
<src_gpc1_rate>src_gpc1_rate - Source IP: increment rate of General Purpose Counter</src_gpc1_rate>
|
||||
<src_http_err_cnt>src_http_err_cnt - Source IP: cumulative number of HTTP errors</src_http_err_cnt>
|
||||
<src_http_err_rate>src_http_err_rate - Source IP: rate of HTTP errors</src_http_err_rate>
|
||||
<src_http_fail_cnt>src_http_fail_cnt - Source IP: cumulative number of HTTP failures</src_http_fail_cnt>
|
||||
<src_http_fail_rate>src_http_fail_rate - Source IP: rate of HTTP failures</src_http_fail_rate>
|
||||
<src_http_req_cnt>src_http_req_cnt - Source IP: number of HTTP requests</src_http_req_cnt>
|
||||
<src_http_req_rate>src_http_req_rate - Source IP: rate of HTTP requests</src_http_req_rate>
|
||||
<src_inc_gpc>src_inc_gpc - Source IP: increment General Purpose Counter</src_inc_gpc>
|
||||
<src_inc_gpc0>src_inc_gpc0 - Source IP: increment General Purpose Counter</src_inc_gpc0>
|
||||
<src_inc_gpc1>src_inc_gpc1 - Source IP: increment General Purpose Counter</src_inc_gpc1>
|
||||
<src_is_local>src_is_local - Source IP is local</src_is_local>
|
||||
<src_kbytes_in>src_kbytes_in - Source IP: amount of data received (in kilobytes)</src_kbytes_in>
|
||||
<src_kbytes_out>src_kbytes_out - Source IP: amount of data sent (in kilobytes)</src_kbytes_out>
|
||||
<src_port>src_port - Source IP: TCP source port</src_port>
|
||||
<src_sess_cnt>src_sess_cnt - Source IP: cumulative number of sessions</src_sess_cnt>
|
||||
<src_sess_rate>src_sess_rate - Source IP: session rate</src_sess_rate>
|
||||
<ssl_c_ca_commonname>ssl_c_ca_commonname - SSL Client certificate issued by CA common-name</ssl_c_ca_commonname>
|
||||
<ssl_c_verify_code>ssl_c_verify_code - SSL Client certificate verify error result</ssl_c_verify_code>
|
||||
<ssl_c_verify>ssl_c_verify - SSL Client certificate is valid</ssl_c_verify>
|
||||
<ssl_fc_sni>ssl_fc_sni - SNI TLS extension matches (locally deciphered)</ssl_fc_sni>
|
||||
<ssl_fc>ssl_fc - Traffic is SSL (locally deciphered)</ssl_fc>
|
||||
<ssl_hello_type>ssl_hello_type - SSL Hello Type</ssl_hello_type>
|
||||
<ssl_sni_beg>ssl_sni_beg - SNI TLS extension starts with (TCP request content inspection)</ssl_sni_beg>
|
||||
<ssl_sni_end>ssl_sni_end - SNI TLS extension ends with (TCP request content inspection)</ssl_sni_end>
|
||||
<ssl_sni_reg>ssl_sni_reg - SNI TLS extension regex (TCP request content inspection)</ssl_sni_reg>
|
||||
<ssl_sni>ssl_sni - SNI TLS extension matches (TCP request content inspection)</ssl_sni>
|
||||
<ssl_sni_sub>ssl_sni_sub - SNI TLS extension contains (TCP request content inspection)</ssl_sni_sub>
|
||||
<stopping>stopping - HAProxy process is currently stopping</stopping>
|
||||
<url_param>url_param - URL parameter contains</url_param>
|
||||
<var>var - Compare the value of a variable</var>
|
||||
<wait_end>wait_end - Inspection period is over</wait_end>
|
||||
<custom_acl>Custom condition (option pass-through)</custom_acl>
|
||||
</OptionValues>
|
||||
</expression>
|
||||
|
|
@ -2076,6 +2077,25 @@
|
|||
<Mask>/^.{1,4096}$/u</Mask>
|
||||
<Required>N</Required>
|
||||
</url_param_value>
|
||||
<var type="TextField">
|
||||
<Mask>/^.{1,4096}$/u</Mask>
|
||||
<Required>N</Required>
|
||||
</var>
|
||||
<var_value type="TextField">
|
||||
<Mask>/^.{1,4096}$/u</Mask>
|
||||
<Required>N</Required>
|
||||
</var_value>
|
||||
<var_comparison type="OptionField">
|
||||
<Required>N</Required>
|
||||
<Default>gt</Default>
|
||||
<OptionValues>
|
||||
<gt>greater than</gt>
|
||||
<ge>greater equal</ge>
|
||||
<eq>equal</eq>
|
||||
<lt>less than</lt>
|
||||
<le>less equal</le>
|
||||
</OptionValues>
|
||||
</var_comparison>
|
||||
<ssl_c_verify_code type="IntegerField">
|
||||
<MinimumValue>0</MinimumValue>
|
||||
<MaximumValue>500000</MaximumValue>
|
||||
|
|
@ -3467,12 +3487,16 @@
|
|||
<ValidationMessage>Related mapfile item not found</ValidationMessage>
|
||||
<Required>N</Required>
|
||||
</mapfile>
|
||||
<converter type="TextField">
|
||||
<Mask>/^.{1,4096}$/u</Mask>
|
||||
<Required>N</Required>
|
||||
</converter>
|
||||
</acl>
|
||||
</acls>
|
||||
<actions>
|
||||
<action type="ArrayField">
|
||||
<enabled type="BooleanField">
|
||||
<default>1</default>
|
||||
<Default>1</Default>
|
||||
<Required>Y</Required>
|
||||
</enabled>
|
||||
<name type="TextField">
|
||||
|
|
@ -3622,6 +3646,7 @@
|
|||
<do-log>do-log</do-log>
|
||||
<do-resolve>do-resolve</do-resolve>
|
||||
<early-hint>early-hint</early-hint>
|
||||
<lua>lua</lua>
|
||||
<normalize-uri>normalize-uri</normalize-uri>
|
||||
<redirect>redirect</redirect>
|
||||
<reject>reject</reject>
|
||||
|
|
@ -3665,7 +3690,7 @@
|
|||
<track-sc1>track-sc1</track-sc1>
|
||||
<track-sc2>track-sc2</track-sc2>
|
||||
<unset-var>unset-var</unset-var>
|
||||
<use-service>use-service</use-service>
|
||||
<use-service>use-service - use a lua service</use-service>
|
||||
<wait-for-body>wait-for-body</wait-for-body>
|
||||
<wait-for-handshake>wait-for-handshake</wait-for-handshake>
|
||||
</OptionValues>
|
||||
|
|
@ -3687,6 +3712,7 @@
|
|||
<del-map>del-map</del-map>
|
||||
<deny>deny</deny>
|
||||
<do-log>do-log</do-log>
|
||||
<lua>lua</lua>
|
||||
<redirect>redirect</redirect>
|
||||
<replace-header>replace-header</replace-header>
|
||||
<replace-value>replace-value</replace-value>
|
||||
|
|
@ -3780,7 +3806,7 @@
|
|||
<content_track-sc1>content track-sc1</content_track-sc1>
|
||||
<content_track-sc2>content track-sc2</content_track-sc2>
|
||||
<content_unset-var>content unset-var</content_unset-var>
|
||||
<content_use-service>content use-service</content_use-service>
|
||||
<content_use-service>content use-service - use a lua service</content_use-service>
|
||||
<inspect-delay>inspect-delay</inspect-delay>
|
||||
<session_accept>session accept</session_accept>
|
||||
<session_attach-srv>session attach-srv</session_attach-srv>
|
||||
|
|
@ -4142,6 +4168,25 @@
|
|||
<ValidationMessage>Please specify a value between 0 and 99.</ValidationMessage>
|
||||
<Required>N</Required>
|
||||
</sc_number>
|
||||
<mapfile type="ModelRelationField">
|
||||
<Model>
|
||||
<template>
|
||||
<source>OPNsense.HAProxy.HAProxy</source>
|
||||
<items>mapfiles.mapfile</items>
|
||||
<display>name</display>
|
||||
</template>
|
||||
</Model>
|
||||
<ValidationMessage>Related mapfile item not found</ValidationMessage>
|
||||
<Required>N</Required>
|
||||
</mapfile>
|
||||
<map_default type="TextField">
|
||||
<Mask>/^.{1,4096}$/u</Mask>
|
||||
<Required>N</Required>
|
||||
</map_default>
|
||||
<sample_fetch type="TextField">
|
||||
<Mask>/^.{1,4096}$/u</Mask>
|
||||
<Required>N</Required>
|
||||
</sample_fetch>
|
||||
</action>
|
||||
</actions>
|
||||
<luas>
|
||||
|
|
@ -4309,18 +4354,23 @@
|
|||
<Required>Y</Required>
|
||||
<Default>dom</Default>
|
||||
<OptionValues>
|
||||
<beg>beg – key begins with requested value</beg>
|
||||
<dom>dom – Domains</dom>
|
||||
<end>end – key ends with requested value</end>
|
||||
<int>int – Integers</int>
|
||||
<ip>ip – IPs</ip>
|
||||
<reg>reg – Regular Expressions</reg>
|
||||
<str>str – Strings</str>
|
||||
<beg>beg - key begins with requested value</beg>
|
||||
<dom>dom - Domains</dom>
|
||||
<end>end - key ends with requested value</end>
|
||||
<int>int - Integers</int>
|
||||
<ip>ip - IPs</ip>
|
||||
<reg>reg - Regular Expressions</reg>
|
||||
<str>str - Strings</str>
|
||||
<sub>sub - substring matches requested value</sub>
|
||||
</OptionValues>
|
||||
</type>
|
||||
<content type="TextField">
|
||||
<Required>Y</Required>
|
||||
<Required>N</Required>
|
||||
</content>
|
||||
<url type="UrlField">
|
||||
<Mask>/^.{1,4096}$/u</Mask>
|
||||
<Required>N</Required>
|
||||
</url>
|
||||
</mapfile>
|
||||
</mapfiles>
|
||||
<groups>
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ class M5_0_0 extends BaseModelMigration
|
|||
$action->http_response_option = (string)$action->http_response_set_status_code . $status_reason;
|
||||
$action->http_response_set_status_code = null;
|
||||
$action->http_response_set_status_reason = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'http-response_set-var':
|
||||
$action->type = 'http-response';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{#
|
||||
|
||||
Copyright (C) 2021 Frank Wall
|
||||
Copyright (C) 2021-2026 Frank Wall
|
||||
OPNsense® is Copyright © 2014 – 2016 by Deciso B.V.
|
||||
All rights reserved.
|
||||
|
||||
|
|
@ -29,16 +29,17 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* show HAProxy config
|
||||
*/
|
||||
function update_showconf() {
|
||||
ajaxCall(url="/api/haproxy/export/config/", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/export/config/", {}, function(data, status) {
|
||||
if (data['response'] && data['response'].trim()) {
|
||||
$("#showconf").text(data['response']);
|
||||
} else {
|
||||
conf_help = "<br><span style=\"color: #000000; white-space: pre-wrap; font-family: monospace;\"> {{ lang._('Config file not found. Run a syntax check to create it.') }}</span><br>";
|
||||
$("#showconfempty").append(conf_help);
|
||||
$("#showconfempty").append('<br><span class="conf-message"> {{ lang._('Config file not found. Run a syntax check to create it.') }}</span><br>');
|
||||
$("#showconf").hide();
|
||||
}
|
||||
});
|
||||
|
|
@ -49,29 +50,16 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
* show HAProxy config diff
|
||||
*/
|
||||
function update_showdiff() {
|
||||
ajaxCall(url="/api/haproxy/export/diff/", sendData={}, callback=function(data,status) {
|
||||
diff = '';
|
||||
ajaxCall("/api/haproxy/export/diff/", {}, function(data, status) {
|
||||
const diffClasses = {'+': 'diff-add', '-': 'diff-remove', '@': 'diff-hunk'};
|
||||
let diff = '';
|
||||
if (data['response'] && data['response'].trim()) {
|
||||
var lines = data['response'].split("\n");
|
||||
$.each(lines, function(n, line) {
|
||||
switch(line.substring(0,1)) {
|
||||
case '+':
|
||||
color = '#3bbb33';
|
||||
break;
|
||||
case '-':
|
||||
color = '#c13928';
|
||||
break;
|
||||
case '@':
|
||||
color = '#3bb9c3';
|
||||
break;
|
||||
default:
|
||||
color = '#000000';
|
||||
}
|
||||
diff += '<span style="color: ' + color + '; white-space: pre-wrap; font-family: monospace;">' + line + '</span><br>';
|
||||
|
||||
data['response'].split("\n").forEach(function(line) {
|
||||
const cssClass = diffClasses[line.substring(0, 1)] || 'diff-context';
|
||||
diff += `<span class="${cssClass}">${$('<span/>').text(line).html()}</span><br>`;
|
||||
});
|
||||
} else {
|
||||
diff = "<br><span style=\"color: #000000; white-space: pre-wrap; font-family: monospace;\"> {{ lang._('New and old config files are identical.') }}</span><br>";
|
||||
diff = '<br><span class="diff-context"> {{ lang._('New and old config files are identical.') }}</span><br>';
|
||||
}
|
||||
$("#showdiff").append(diff);
|
||||
});
|
||||
|
|
@ -83,11 +71,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
*/
|
||||
$('[id*="exportbtn"]').each(function(){
|
||||
$(this).click(function(){
|
||||
var type = $(this).data("type");
|
||||
ajaxGet("/api/haproxy/export/download/"+type+"/", {}, function(data, status){
|
||||
const type = $(this).data("type");
|
||||
ajaxGet("/api/haproxy/export/download/" + type + "/", {}, function(data, status){
|
||||
if (data.filename !== undefined) {
|
||||
var link = $('<a></a>')
|
||||
.attr('href','data:'+data.filetype+';base64,' + data.content)
|
||||
const link = $('<a></a>')
|
||||
.attr('href', 'data:' + data.filetype + ';base64,' + data.content)
|
||||
.attr('download', data.filename)
|
||||
.appendTo('body');
|
||||
|
||||
|
|
@ -101,18 +89,26 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
});
|
||||
|
||||
// update history on tab state and implement navigation
|
||||
if(window.location.hash != "") {
|
||||
$('a[href="' + window.location.hash + '"]').click()
|
||||
if (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);
|
||||
});
|
||||
$(window).on('hashchange', function(e) {
|
||||
$('a[href="' + window.location.hash + '"]').click()
|
||||
$('a[href="' + window.location.hash + '"]').click();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.diff-add { color: #3bbb33; white-space: pre-wrap; font-family: monospace; }
|
||||
.diff-remove { color: #c13928; white-space: pre-wrap; font-family: monospace; }
|
||||
.diff-hunk { color: #3bb9c3; white-space: pre-wrap; font-family: monospace; }
|
||||
.diff-context { white-space: pre-wrap; font-family: monospace; }
|
||||
.conf-message { white-space: pre-wrap; font-family: monospace; }
|
||||
</style>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" href="#export"><b>{{ lang._('Config Export') }}</b></a></li>
|
||||
<li><a data-toggle="tab" href="#diff">{{ lang._('Config Diff') }}</a></li>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{#
|
||||
|
||||
Copyright (C) 2016-2021 Frank Wall
|
||||
Copyright (C) 2016-2026 Frank Wall
|
||||
OPNsense® is Copyright © 2014 – 2015 by Deciso B.V.
|
||||
All rights reserved.
|
||||
|
||||
|
|
@ -30,16 +30,17 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
<script>
|
||||
|
||||
$( document ).ready(function() {
|
||||
'use strict';
|
||||
|
||||
// get general HAProxy settings
|
||||
var data_get_map = {'frm_haproxy':"/api/haproxy/settings/get"};
|
||||
const data_get_map = {'frm_haproxy':"/api/haproxy/settings/get"};
|
||||
|
||||
// load initial data
|
||||
mapDataToFormUI(data_get_map).done(function(){
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
// request service status on load and update status box
|
||||
ajaxCall(url="/api/haproxy/service/status", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/service/status", {}, function(data, status) {
|
||||
updateServiceStatusUI(data['status']);
|
||||
});
|
||||
});
|
||||
|
|
@ -226,7 +227,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
// hook into on-show event for dialog to extend layout.
|
||||
$('#DialogAcl').on('shown.bs.modal', function (e) {
|
||||
$("#acl\\.expression").change(function(){
|
||||
var service_id = 'table_' + $(this).val();
|
||||
const service_id = 'table_' + $(this).val();
|
||||
$(".expression_table").hide();
|
||||
$("."+service_id).show();
|
||||
});
|
||||
|
|
@ -236,7 +237,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
// hook into on-show event for dialog to extend layout.
|
||||
$('#DialogAction').on('shown.bs.modal', function (e) {
|
||||
$("#action\\.type").change(function(){
|
||||
var service_id = 'table_' + $(this).val();
|
||||
const service_id = 'table_' + $(this).val();
|
||||
$(".type_table").hide();
|
||||
$("."+service_id).show();
|
||||
});
|
||||
|
|
@ -246,21 +247,21 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
// hook into on-show event for dialog to extend layout.
|
||||
$('#DialogBackend').on('shown.bs.modal', function (e) {
|
||||
$("#backend\\.mode").change(function(){
|
||||
var service_id = 'table_' + $(this).val();
|
||||
const service_id = 'table_' + $(this).val();
|
||||
$(".mode_table").hide();
|
||||
$("."+service_id).show();
|
||||
});
|
||||
$("#backend\\.mode").change();
|
||||
|
||||
$("#backend\\.healthCheckEnabled").change(function(){
|
||||
var service_id = 'table_healthcheck_' + $(this).is(':checked');
|
||||
const service_id = 'table_healthcheck_' + $(this).is(':checked');
|
||||
$(".healthcheck_table").hide();
|
||||
$("."+service_id).show();
|
||||
});
|
||||
$("#backend\\.healthCheckEnabled").change();
|
||||
|
||||
$("#backend\\.persistence").change(function(){
|
||||
var persistence_id = 'table_persistence_' + $(this).val();
|
||||
const persistence_id = 'table_persistence_' + $(this).val();
|
||||
$(".persistence_table").hide();
|
||||
$("."+persistence_id).show();
|
||||
});
|
||||
|
|
@ -270,7 +271,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
// hook into on-show event for dialog to extend layout.
|
||||
$('#DialogFrontend').on('shown.bs.modal', function (e) {
|
||||
$("#frontend\\.mode").change(function(){
|
||||
var service_id = 'table_' + $(this).val();
|
||||
const service_id = 'table_' + $(this).val();
|
||||
$(".mode_table").hide();
|
||||
$("."+service_id).show();
|
||||
});
|
||||
|
|
@ -278,7 +279,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
// show/hide SSL offloading
|
||||
$("#frontend\\.ssl_enabled").change(function(){
|
||||
var service_id = 'table_ssl_' + $(this).is(':checked');
|
||||
const service_id = 'table_ssl_' + $(this).is(':checked');
|
||||
$(".table_ssl").hide();
|
||||
$("."+service_id).show();
|
||||
});
|
||||
|
|
@ -286,7 +287,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
// show/hide advanced SSL settings
|
||||
$("#frontend\\.ssl_advancedEnabled").change(function(){
|
||||
var service_id = 'table_ssl_advanced_' + $(this).is(':checked');
|
||||
const service_id = 'table_ssl_advanced_' + $(this).is(':checked');
|
||||
$(".table_ssl_advanced").hide();
|
||||
$("."+service_id).show();
|
||||
});
|
||||
|
|
@ -296,7 +297,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
// hook into on-show event for dialog to extend layout.
|
||||
$('#DialogHealthcheck').on('shown.bs.modal', function (e) {
|
||||
$("#healthcheck\\.type").change(function(){
|
||||
var service_id = 'table_' + $(this).val();
|
||||
const service_id = 'table_' + $(this).val();
|
||||
$(".type_table").hide();
|
||||
$("."+service_id).show();
|
||||
});
|
||||
|
|
@ -306,7 +307,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
// hook into on-show event for dialog to extend layout.
|
||||
$('#DialogServer').on('shown.bs.modal', function (e) {
|
||||
$("#server\\.type").change(function(){
|
||||
var service_id = 'table_server_type_' + $(this).val();
|
||||
const service_id = 'table_server_type_' + $(this).val();
|
||||
$(".table_server_type").hide();
|
||||
$("."+service_id).show();
|
||||
});
|
||||
|
|
@ -321,31 +322,20 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
$('[id*="reconfigureAct"]').each(function(){
|
||||
$(this).click(function(){
|
||||
// set progress animation
|
||||
$('[id*="reconfigureAct_progress"]').each(function(){
|
||||
$(this).addClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
$('[id*="reconfigureAct_progress"]').addClass("fa fa-spinner fa-pulse");
|
||||
// first run syntax check to catch critical errors
|
||||
ajaxCall(url="/api/haproxy/service/configtest", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/service/configtest", {}, function(data, status) {
|
||||
// show warning in case of critical errors
|
||||
if (data['result'].indexOf('ALERT') > -1) {
|
||||
$('[id*="reconfigureAct_progress"]').removeClass("fa fa-spinner fa-pulse");
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
title: "{{ lang._('HAProxy configtest found critical errors') }}",
|
||||
message: "{{ lang._('The HAProxy service may not be able to start due to critical errors. Run syntax check for further details or review the changes in the %sConfiguration Diff%s.')|format('<a href=\"/ui/haproxy/export#diff\">','</a>') }}",
|
||||
buttons: [{
|
||||
icon: 'fa fa-trash-o',
|
||||
label: '{{ lang._('Abort') }}',
|
||||
action: function(dlg){
|
||||
// when done, disable progress animation
|
||||
$('[id*="reconfigureAct_progress"]').each(function(){
|
||||
$(this).removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
dlg.close();
|
||||
}
|
||||
}]
|
||||
draggable: true
|
||||
});
|
||||
} else {
|
||||
ajaxCall(url="/api/haproxy/service/reconfigure", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/service/reconfigure", {}, function(data, status) {
|
||||
if (status != "success" || data['status'] != 'ok') {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_WARNING,
|
||||
|
|
@ -356,13 +346,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
} else {
|
||||
// reload page to hide pending changes reminder
|
||||
setTimeout(function () {
|
||||
window.location.reload(true)
|
||||
window.location.reload(true);
|
||||
}, 300);
|
||||
}
|
||||
// when done, disable progress animation
|
||||
$('[id*="reconfigureAct_progress"]').each(function(){
|
||||
$(this).removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
$('[id*="reconfigureAct_progress"]').removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -373,15 +361,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
$('[id*="configtestAct"]').each(function(){
|
||||
$(this).click(function(){
|
||||
// set progress animation
|
||||
$('[id*="configtestAct_progress"]').each(function(){
|
||||
$(this).addClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
$('[id*="configtestAct_progress"]').addClass("fa fa-spinner fa-pulse");
|
||||
|
||||
ajaxCall(url="/api/haproxy/service/configtest", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/service/configtest", {}, function(data, status) {
|
||||
// when done, disable progress animation
|
||||
$('[id*="configtestAct_progress"]').each(function(){
|
||||
$(this).removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
$('[id*="configtestAct_progress"]').removeClass("fa fa-spinner fa-pulse");
|
||||
|
||||
if (data['result'].indexOf('ALERT') > -1) {
|
||||
BootstrapDialog.show({
|
||||
|
|
@ -413,17 +397,15 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
$('[id*="saveAndTestAct"]').each(function(){
|
||||
$(this).click(function(){
|
||||
// extract the form id from the button id
|
||||
var frm_id = "frm_" + $(this).attr("id").split('_')[1]
|
||||
const frm_id = "frm_" + $(this).attr("id").split('_')[1];
|
||||
|
||||
// save data for this tab
|
||||
saveFormToEndpoint(url="/api/haproxy/settings/set",formid=frm_id,callback_ok=function(){
|
||||
saveFormToEndpoint("/api/haproxy/settings/set", frm_id, function(){
|
||||
// set progress animation
|
||||
$('[id*="saveAndTestAct_progress"]').each(function(){
|
||||
$(this).addClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
$('[id*="saveAndTestAct_progress"]').addClass("fa fa-spinner fa-pulse");
|
||||
|
||||
// on correct save, perform config test
|
||||
ajaxCall(url="/api/haproxy/service/configtest", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/service/configtest", {}, function(data, status) {
|
||||
if (data['result'].indexOf('ALERT') > -1) {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
|
|
@ -448,9 +430,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
}
|
||||
|
||||
// when done, disable progress animation
|
||||
$('[id*="saveAndTestAct_progress"]').each(function(){
|
||||
$(this).removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
$('[id*="saveAndTestAct_progress"]').removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -460,57 +440,36 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
$('[id*="saveAndReconfigureAct"]').each(function(){
|
||||
$(this).click(function(){
|
||||
// extract the form id from the button id
|
||||
var frm_id = "frm_" + $(this).attr("id").split('_')[1]
|
||||
const frm_id = "frm_" + $(this).attr("id").split('_')[1];
|
||||
|
||||
// save data for this tab
|
||||
saveFormToEndpoint(url="/api/haproxy/settings/set",formid=frm_id,callback_ok=function(){
|
||||
saveFormToEndpoint("/api/haproxy/settings/set", frm_id, function(){
|
||||
// set progress animation
|
||||
$('[id*="saveAndReconfigureAct_progress"]').each(function(){
|
||||
$(this).addClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
$('[id*="saveAndReconfigureAct_progress"]').addClass("fa fa-spinner fa-pulse");
|
||||
|
||||
// on correct save, perform config test
|
||||
ajaxCall(url="/api/haproxy/service/configtest", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/service/configtest", {}, function(data, status) {
|
||||
// show warning in case of critical errors
|
||||
if (data['result'].indexOf('ALERT') > -1) {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
title: "{{ lang._('HAProxy config contains critical errors') }}",
|
||||
message: "{{ lang._('The HAProxy service may not be able to start due to critical errors. Try anyway?') }}",
|
||||
buttons: [{
|
||||
label: '{{ lang._('Continue') }}',
|
||||
cssClass: 'btn-primary',
|
||||
action: function(dlg){
|
||||
ajaxCall(url="/api/haproxy/service/reconfigure", sendData={}, callback=function(data,status) {
|
||||
if (status != "success" || data['status'] != 'ok') {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_WARNING,
|
||||
title: "{{ lang._('Error reconfiguring HAProxy') }}",
|
||||
message: data['status'],
|
||||
draggable: true
|
||||
});
|
||||
}
|
||||
});
|
||||
// when done, disable progress animation
|
||||
$('[id*="saveAndReconfigureAct_progress"]').each(function(){
|
||||
$(this).removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
dlg.close();
|
||||
}
|
||||
}, {
|
||||
icon: 'fa fa-trash-o',
|
||||
label: '{{ lang._('Abort') }}',
|
||||
action: function(dlg){
|
||||
// when done, disable progress animation
|
||||
$('[id*="saveAndReconfigureAct_progress"]').each(function(){
|
||||
$(this).removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
dlg.close();
|
||||
}
|
||||
}]
|
||||
});
|
||||
$('[id*="saveAndReconfigureAct_progress"]').removeClass("fa fa-spinner fa-pulse");
|
||||
stdDialogConfirm(
|
||||
"{{ lang._('HAProxy config contains critical errors') }}",
|
||||
"{{ lang._('The HAProxy service may not be able to start due to critical errors. Try anyway?') }}",
|
||||
"{{ lang._('Continue') }}", "{{ lang._('Abort') }}", function() {
|
||||
ajaxCall("/api/haproxy/service/reconfigure", {}, function(data, status) {
|
||||
if (status != "success" || data['status'] != 'ok') {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_WARNING,
|
||||
title: "{{ lang._('Error reconfiguring HAProxy') }}",
|
||||
message: data['status'],
|
||||
draggable: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
ajaxCall(url="/api/haproxy/service/reconfigure", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/service/reconfigure", {}, function(data, status) {
|
||||
if (status != "success" || data['status'] != 'ok') {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_WARNING,
|
||||
|
|
@ -521,13 +480,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
} else {
|
||||
// reload page to hide pending changes reminder
|
||||
setTimeout(function () {
|
||||
window.location.reload(true)
|
||||
window.location.reload(true);
|
||||
}, 300);
|
||||
}
|
||||
// when done, disable progress animation
|
||||
$('[id*="saveAndReconfigureAct_progress"]').each(function(){
|
||||
$(this).removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
$('[id*="saveAndReconfigureAct_progress"]').removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -542,7 +499,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
// show reminder when config has pending changes
|
||||
function pending_changes_reminder() {
|
||||
ajaxCall(url="/api/haproxy/export/diff/", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/export/diff/", {}, function(data, status) {
|
||||
if (data['response'] && data['response'].trim()) {
|
||||
$("#haproxyPendingReminder").show();
|
||||
} else {
|
||||
|
|
@ -553,14 +510,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
pending_changes_reminder();
|
||||
|
||||
// show hint after every config change
|
||||
function add_apply_reminder() {
|
||||
hint_msg = "{{ lang._('After changing settings, please remember to test and apply them with the buttons below.') }}"
|
||||
$('[id*="haproxyChangeMessage"]').each(function(){
|
||||
$(this).append(hint_msg);
|
||||
});
|
||||
|
||||
};
|
||||
add_apply_reminder();
|
||||
const hint_msg = "{{ lang._('After changing settings, please remember to test and apply them with the buttons below.') }}";
|
||||
$('[id*="haproxyChangeMessage"]').append(hint_msg);
|
||||
|
||||
// show or hide the correct buttons depending on which tab is shown
|
||||
// NOTE: This does not work on already shown tabs, so this event must
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{#
|
||||
|
||||
Copyright (C) 2026 Frank Wall
|
||||
Copyright (C) 2021 Andreas Stuerz
|
||||
OPNsense® is Copyright © 2014 – 2016 by Deciso B.V.
|
||||
All rights reserved.
|
||||
|
|
@ -28,37 +29,32 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#}
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
'use strict';
|
||||
|
||||
// Get cronjobs
|
||||
var cronjobs_data_get_map = {'frm_cronjobs':"/api/haproxy/maintenance/get"};
|
||||
const cronjobs_data_get_map = {'frm_cronjobs':"/api/haproxy/maintenance/get"};
|
||||
// load initial data
|
||||
mapDataToFormUI(cronjobs_data_get_map).done(function(data){
|
||||
// Add link to cron job edit page: First iterate over all cron settings.
|
||||
// FIXME: Oh boy, this is ugly. Should be refactored.
|
||||
$.each(data.frm_cronjobs.haproxy.maintenance.cronjobs, function(key, value) {
|
||||
// Check if cron setting is enabled.
|
||||
// Add link to cron job edit page for each enabled cron setting.
|
||||
function addCronLink(key, cronData) {
|
||||
const cron_cfg = key + 'Cron';
|
||||
if (!(cron_cfg in cronData)) {
|
||||
return;
|
||||
}
|
||||
Object.entries(cronData[cron_cfg]).forEach(function([refkey, refvalue]) {
|
||||
if (refvalue.selected == 1) {
|
||||
const content_id = `[id="haproxy.maintenance.cronjobs.${key}"]`;
|
||||
const cron_link = `<br><a href="/ui/cron/item/open/${refkey}"><span class="fa fa-pencil"></span> {{ lang._('Configure cron job') }}</a>`;
|
||||
$(content_id).closest("td").append(cron_link);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const cronData = data.frm_cronjobs.haproxy.maintenance.cronjobs;
|
||||
Object.entries(cronData).forEach(function([key, value]) {
|
||||
if (value == 1) {
|
||||
// Find the matching cron job reference.
|
||||
cron_cfg = key + 'Cron';
|
||||
$.each(data.frm_cronjobs.haproxy.maintenance.cronjobs, function(cronkey, cronvalue) {
|
||||
// Check if it is the correct entry for this cron setting.
|
||||
if (cronkey == cron_cfg) {
|
||||
// Get the cron job UUID.
|
||||
$.each(cronvalue, function(refkey, refvalue) {
|
||||
// Only the "selected" item belongs to this entry.
|
||||
if (refvalue.selected == 1) {
|
||||
// Find the correct container for this cron setting.
|
||||
content_id = "[id=\"haproxy.maintenance.cronjobs." + key + "\"]";
|
||||
$(content_id).each(function(){
|
||||
// Finally add the link to the cron job edit page.
|
||||
cron_link = "<br><a href=\"/ui/cron/item/open/" + refkey + "\"><span class=\"fa fa-pencil\"></span> {{ lang._('Configure cron job') }}</a>";
|
||||
$(this).closest("td").append(cron_link);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
addCronLink(key, cronData);
|
||||
}
|
||||
});
|
||||
|
||||
formatTokenizersUI();
|
||||
|
|
@ -69,27 +65,22 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
$('[id*="saveAndReconfigureAct"]').each(function(){
|
||||
$(this).click(function(){
|
||||
// set progress animation
|
||||
$('[id*="saveAndReconfigureAct_progress"]').each(function(){
|
||||
$(this).addClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
$('[id*="saveAndReconfigureAct_progress"]').addClass("fa fa-spinner fa-pulse");
|
||||
|
||||
// extract the form id from the button id
|
||||
var frm_id = "frm_" + $(this).attr("id").split('_')[1]
|
||||
const frm_id = "frm_" + $(this).attr("id").split('_')[1];
|
||||
|
||||
// save data for this tab
|
||||
saveFormToEndpoint(url="/api/haproxy/maintenance/set",formid=frm_id,callback_ok=function(){
|
||||
saveFormToEndpoint("/api/haproxy/maintenance/set", frm_id, function(){
|
||||
// Handle cron integration
|
||||
ajaxCall(url="/api/haproxy/maintenance/fetch_cron_integration", sendData={}, callback=function(data,status) {
|
||||
ajaxCall("/api/haproxy/maintenance/fetch_cron_integration", {}, function(data, status) {
|
||||
});
|
||||
|
||||
// when done, disable progress animation
|
||||
$('[id*="saveAndReconfigureAct_progress"]').each(function(){
|
||||
$(this).removeClass("fa fa-spinner fa-pulse");
|
||||
// reload page to show or hide links to cron edit page
|
||||
setTimeout(function () {
|
||||
window.location.reload(true)
|
||||
}, 300);
|
||||
});
|
||||
// when done, disable progress animation and reload to show/hide cron links
|
||||
$('[id*="saveAndReconfigureAct_progress"]').removeClass("fa fa-spinner fa-pulse");
|
||||
setTimeout(function () {
|
||||
window.location.reload(true);
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -127,7 +118,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
}
|
||||
|
||||
function showDiffDialog(payload) {
|
||||
$.post('/api/haproxy/maintenance/cert_diff', payload, function(data) {
|
||||
ajaxCall("/api/haproxy/maintenance/cert_diff", payload, function(data, status) {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_INFO,
|
||||
title: "{{ lang._('Diff between configured and active SSL certificates') }}",
|
||||
|
|
@ -143,19 +134,18 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
}
|
||||
|
||||
function applyDiffDialog(payload, requested_count) {
|
||||
$.post('/api/haproxy/maintenance/cert_actions', payload, function(data_actions) {
|
||||
question = ''
|
||||
question += `<pre>${data_actions}</pre>`;
|
||||
ajaxCall("/api/haproxy/maintenance/cert_actions", payload, function(data_actions, status) {
|
||||
let question = `<pre>${data_actions}</pre>`;
|
||||
question += '<b>{{ lang._('Apply SSL certificates to HAProxy?') }}</b></br></br>';
|
||||
|
||||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
question,
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
$.post('/api/haproxy/maintenance/cert_sync', payload, function(data) {
|
||||
modified_count = data.result.add_count + data.result.remove_count + data.result.update_count;
|
||||
ajaxCall("/api/haproxy/maintenance/cert_sync", payload, function(data, status) {
|
||||
const modified_count = data.result.add_count + data.result.remove_count + data.result.update_count;
|
||||
|
||||
if (requested_count != modified_count) {
|
||||
var error_msg = syncErrorMessage(data.result.modified, data.result.deleted);
|
||||
const error_msg = syncErrorMessage(data.result.modified, data.result.deleted);
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
title: "{{ lang._('Error applying SSL certificates to HAProxy') }}",
|
||||
|
|
@ -174,8 +164,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
});
|
||||
}
|
||||
|
||||
$("#grid-certificates").bootgrid('destroy');
|
||||
var grid_certificates = $("#grid-certificates").UIBootgrid({
|
||||
const grid_certificates = $("#grid-certificates").UIBootgrid({
|
||||
search: '/api/haproxy/maintenance/search_certificate_diff',
|
||||
options: {
|
||||
ajax: true,
|
||||
|
|
@ -188,83 +177,63 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
},
|
||||
formatters: {
|
||||
"commands": function (column, row) {
|
||||
buttons = ""
|
||||
buttons += "<button type=\"button\" data-action=\"showDiff\" title=\"{{ lang._('Show diff') }}\" class=\"btn btn-xs btn-default\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-info-circle\"></span></button>"
|
||||
buttons += " <button type=\"button\" data-action=\"applyDiff\" title=\"{{ lang._('Apply changes') }}\" class=\"btn btn-xs btn-default\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-refresh\"></span></button>"
|
||||
let buttons = "";
|
||||
buttons += `<button type="button" data-action="showDiff" title="{{ lang._('Show diff') }}" class="btn btn-xs btn-default" data-row-id="${row.id}"><span class="fa fa-info-circle"></span></button>`;
|
||||
buttons += ` <button type="button" data-action="applyDiff" title="{{ lang._('Apply changes') }}" class="btn btn-xs btn-default" data-row-id="${row.id}"><span class="fa fa-refresh"></span></button>`;
|
||||
return buttons;
|
||||
},
|
||||
},
|
||||
}
|
||||
}).on("loaded.rs.jquery.bootgrid", function(){
|
||||
grid_certificates.find("*[data-action=showDiff]").off().on("click", function(e) {
|
||||
var row_id = $(this).data("row-id");
|
||||
var frontend_ids = row_id;
|
||||
var payload = {
|
||||
'frontend_ids': frontend_ids,
|
||||
};
|
||||
showDiffDialog(payload);
|
||||
const row_id = $(this).data("row-id");
|
||||
showDiffDialog({'frontend_ids': row_id});
|
||||
});
|
||||
|
||||
grid_certificates.find("*[data-action=applyDiff]").off().on("click", function(e) {
|
||||
var row_id = $(this).data("row-id");
|
||||
var rows = $("#grid-certificates").bootgrid("getCurrentRows");
|
||||
var row = rows.filter(function(row) {
|
||||
return row.id == row_id;
|
||||
const row_id = $(this).data("row-id");
|
||||
const rows = $("#grid-certificates").bootgrid("getCurrentRows");
|
||||
const row = rows.filter(function(r) {
|
||||
return r.id == row_id;
|
||||
})[0];
|
||||
var requested_count = row.total_count;
|
||||
var frontend_ids = row.id
|
||||
var payload = {
|
||||
'frontend_ids': frontend_ids,
|
||||
};
|
||||
|
||||
applyDiffDialog(payload, requested_count);
|
||||
applyDiffDialog({'frontend_ids': row.id}, row.total_count);
|
||||
});
|
||||
|
||||
grid_certificates.find("*[data-action=showDiffBulk]").off().on("click", function(e) {
|
||||
var rows = $("#grid-certificates").bootgrid("getSelectedRows");
|
||||
var payload = {
|
||||
'frontend_ids': rows.join()
|
||||
};
|
||||
const rows = $("#grid-certificates").bootgrid("getSelectedRows");
|
||||
if (rows != undefined && rows.length > 0) {
|
||||
showDiffDialog(payload);
|
||||
showDiffDialog({'frontend_ids': rows.join()});
|
||||
}
|
||||
});
|
||||
|
||||
grid_certificates.find("*[data-action=applyDiffBulk]").off().on("click", function(e) {
|
||||
var rows = $("#grid-certificates").bootgrid("getSelectedRows");
|
||||
var frontend_ids = rows.join();
|
||||
var all_rows = $("#grid-certificates").bootgrid("getCurrentRows");
|
||||
var requested_count = 0;
|
||||
const rows = $("#grid-certificates").bootgrid("getSelectedRows");
|
||||
const all_rows = $("#grid-certificates").bootgrid("getCurrentRows");
|
||||
let requested_count = 0;
|
||||
all_rows.forEach(function(row) {
|
||||
if (rows.indexOf(row.id) != -1) {
|
||||
requested_count = requested_count + row.total_count;
|
||||
requested_count += row.total_count;
|
||||
}
|
||||
});
|
||||
var payload = {
|
||||
'frontend_ids': frontend_ids,
|
||||
};
|
||||
if (rows != undefined && rows.length > 0) {
|
||||
applyDiffDialog(payload, requested_count);
|
||||
applyDiffDialog({'frontend_ids': rows.join()}, requested_count);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Apply all changes
|
||||
$("*[data-action=applyDiffAll]").off().on("click", function(e) {
|
||||
$('[id*="applyDiffAll_progress"]').each(function(){
|
||||
$(this).addClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
var all_rows = $("#grid-certificates").bootgrid("getCurrentRows");
|
||||
var requested_count = 0;
|
||||
$('[id*="applyDiffAll_progress"]').addClass("fa fa-spinner fa-pulse");
|
||||
const all_rows = $("#grid-certificates").bootgrid("getCurrentRows");
|
||||
let requested_count = 0;
|
||||
all_rows.forEach(function(row) {
|
||||
requested_count = requested_count + row.total_count;
|
||||
requested_count += row.total_count;
|
||||
});
|
||||
|
||||
var payload = {};
|
||||
$.post('/api/haproxy/maintenance/cert_sync_bulk', payload, function(data) {
|
||||
modified_count = data.result.add_count + data.result.remove_count + data.result.update_count;
|
||||
ajaxCall("/api/haproxy/maintenance/cert_sync_bulk", {}, function(data, status) {
|
||||
const modified_count = data.result.add_count + data.result.remove_count + data.result.update_count;
|
||||
if (requested_count != modified_count) {
|
||||
var error_msg = syncErrorMessage(data.result.modified, data.result.deleted);
|
||||
const error_msg = syncErrorMessage(data.result.modified, data.result.deleted);
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
title: "{{ lang._('Error applying SSL certificates to HAProxy') }}",
|
||||
|
|
@ -283,8 +252,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
});
|
||||
|
||||
// grid-status
|
||||
$("#grid-status").bootgrid('destroy');
|
||||
var grid_status = $("#grid-status").UIBootgrid({
|
||||
const grid_status = $("#grid-status").UIBootgrid({
|
||||
search: '/api/haproxy/maintenance/search_server',
|
||||
options: {
|
||||
ajax: true,
|
||||
|
|
@ -297,11 +265,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
},
|
||||
formatters: {
|
||||
"commands": function (column, row) {
|
||||
buttons = ""
|
||||
buttons += "<button type=\"button\" title=\"{{ lang._('Set state to ready') }}\" class=\"btn btn-xs btn-default command-set-state\" data-state=\"ready\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-check\"></span></button>"
|
||||
buttons += " <button type=\"button\" title=\"{{ lang._('Set state to drain') }}\" class=\"btn btn-xs btn-default command-set-state\" data-state=\"drain\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-sort-amount-desc\"></span></button>"
|
||||
buttons += " <button type=\"button\" title=\"{{ lang._('Set state to maintenance') }}\" class=\"btn btn-xs btn-default command-set-state\" data-state=\"maint\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-wrench\"></span></button>"
|
||||
buttons += " <button type=\"button\" title=\"{{ lang._('Change server weight') }}\" class=\"btn btn-xs btn-default command-set-weight\" data-weight=\"" + row.weight + "\" data-row-id=\"" + row.id + "\"><span class=\"fa fa-balance-scale\"></span></button>"
|
||||
let buttons = "";
|
||||
buttons += `<button type="button" title="{{ lang._('Set state to ready') }}" class="btn btn-xs btn-default command-set-state" data-state="ready" data-row-id="${row.id}"><span class="fa fa-check"></span></button>`;
|
||||
buttons += ` <button type="button" title="{{ lang._('Set state to drain') }}" class="btn btn-xs btn-default command-set-state" data-state="drain" data-row-id="${row.id}"><span class="fa fa-sort-amount-desc"></span></button>`;
|
||||
buttons += ` <button type="button" title="{{ lang._('Set state to maintenance') }}" class="btn btn-xs btn-default command-set-state" data-state="maint" data-row-id="${row.id}"><span class="fa fa-wrench"></span></button>`;
|
||||
buttons += ` <button type="button" title="{{ lang._('Change server weight') }}" class="btn btn-xs btn-default command-set-weight" data-weight="${row.weight}" data-row-id="${row.id}"><span class="fa fa-balance-scale"></span></button>`;
|
||||
return buttons;
|
||||
},
|
||||
},
|
||||
|
|
@ -309,24 +277,20 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
}).on("loaded.rs.jquery.bootgrid", function(){
|
||||
// set single - server state
|
||||
grid_status.find(".command-set-state").off().on("click", function(e) {
|
||||
var uuid = $(this).data("row-id");
|
||||
var backend = uuid.split("/")[0];
|
||||
var server = uuid.split("/")[1];
|
||||
var state = $(this).data("state");
|
||||
var payload = {
|
||||
'backend': backend,
|
||||
'server': server,
|
||||
'state': state
|
||||
};
|
||||
const uuid = $(this).data("row-id");
|
||||
const backend = uuid.split("/")[0];
|
||||
const server = uuid.split("/")[1];
|
||||
const state = $(this).data("state");
|
||||
const payload = {'backend': backend, 'server': server, 'state': state};
|
||||
|
||||
question = '<b>{{ lang._('Server: ') }}' + uuid + '</b></br>';
|
||||
question += '<b>{{ lang._('State: ') }}' + state + '</b></br></br>';
|
||||
let question = `<b>{{ lang._('Server: ') }}${uuid}</b></br>`;
|
||||
question += `<b>{{ lang._('State: ') }}${state}</b></br></br>`;
|
||||
question += '{{ lang._('Set administrative state for this server?') }} </br></br>';
|
||||
|
||||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
question,
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
$.post('/api/haproxy/maintenance/server_state', payload, function(data) {
|
||||
ajaxCall("/api/haproxy/maintenance/server_state", payload, function(data, status) {
|
||||
if (data.status != 'ok') {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
|
|
@ -348,15 +312,15 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
// set single - server weight
|
||||
grid_status.find(".command-set-weight").off().on("click", function(e) {
|
||||
var uuid = $(this).data("row-id");
|
||||
var backend = uuid.split("/")[0];
|
||||
var server = uuid.split("/")[1];
|
||||
var currentWeight = $(this).data("weight");
|
||||
const uuid = $(this).data("row-id");
|
||||
const backend = uuid.split("/")[0];
|
||||
const server = uuid.split("/")[1];
|
||||
const currentWeight = $(this).data("weight");
|
||||
|
||||
question = '<b>{{ lang._('Server: ') }}' + uuid + '</b></br></br>';
|
||||
let question = `<b>{{ lang._('Server: ') }}${uuid}</b></br></br>`;
|
||||
question += '<b>{{ lang._('Weight: ') }}</b>';
|
||||
question += '<div class="form-group" style="display: block;">';
|
||||
question += '<input class="form-control" id="newWeight" value="' + currentWeight + '" type="text"/>';
|
||||
question += `<input class="form-control" id="newWeight" value="${currentWeight}" type="text"/>`;
|
||||
question += '</div>';
|
||||
question += '{{ lang._('Set weight for this server?') }} </br></br>';
|
||||
|
||||
|
|
@ -364,13 +328,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
question,
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
|
||||
var payload = {
|
||||
'backend': backend,
|
||||
'server': server,
|
||||
'weight': $("#newWeight").val()
|
||||
const payload = {
|
||||
'backend': backend,
|
||||
'server': server,
|
||||
'weight': $("#newWeight").val()
|
||||
};
|
||||
|
||||
$.post('/api/haproxy/maintenance/server_weight', payload, function(data) {
|
||||
ajaxCall("/api/haproxy/maintenance/server_weight", payload, function(data, status) {
|
||||
if (data.status != 'ok') {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
|
|
@ -392,28 +356,23 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
// set bulk - server state
|
||||
grid_status.find("*[data-action=setStateBulk]").off().on("click", function(e) {
|
||||
var rows = $("#grid-status").bootgrid("getSelectedRows");
|
||||
var server_ids = rows.join()
|
||||
var state = $(this).data("state");
|
||||
var payload = {
|
||||
'server_ids': server_ids,
|
||||
'state': state
|
||||
};
|
||||
const rows = $("#grid-status").bootgrid("getSelectedRows");
|
||||
const state = $(this).data("state");
|
||||
const payload = {'server_ids': rows.join(), 'state': state};
|
||||
|
||||
if (rows != undefined && rows.length > 0) {
|
||||
question = '<b>{{ lang._('Selected server: ') }}</b></br>';
|
||||
question += '<ul>';
|
||||
$.each(rows, function(key, id){
|
||||
question += '<li>' + id + '</li>';
|
||||
let question = '<b>{{ lang._('Selected server: ') }}</b></br><ul>';
|
||||
rows.forEach(function(id) {
|
||||
question += `<li>${id}</li>`;
|
||||
});
|
||||
question += '</ul>';
|
||||
question += '<b>{{ lang._('State: ') }}' + state + '</b></br></br>';
|
||||
question += `<b>{{ lang._('State: ') }}${state}</b></br></br>`;
|
||||
question += '{{ lang._('Set administrative state for all selected servers?') }} </br></br>';
|
||||
|
||||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
question,
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
$.post('/api/haproxy/maintenance/server_state_bulk', payload, function(data) {
|
||||
ajaxCall("/api/haproxy/maintenance/server_state_bulk", payload, function(data, status) {
|
||||
if (data.status != 'ok') {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
|
|
@ -439,14 +398,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
// set bulk - server weight
|
||||
grid_status.find("*[data-action=setWeightBulk]").off().on("click", function(e) {
|
||||
var rows = $("#grid-status").bootgrid("getSelectedRows");
|
||||
var server_ids = rows.join()
|
||||
const rows = $("#grid-status").bootgrid("getSelectedRows");
|
||||
const server_ids = rows.join();
|
||||
|
||||
if (rows != undefined && rows.length > 0) {
|
||||
question = '<b>{{ lang._('Selected server: ') }}</b></br>';
|
||||
question += '<ul>';
|
||||
$.each(rows, function(key, id){
|
||||
question += '<li>' + id + '</li>';
|
||||
let question = '<b>{{ lang._('Selected server: ') }}</b></br><ul>';
|
||||
rows.forEach(function(id) {
|
||||
question += `<li>${id}</li>`;
|
||||
});
|
||||
question += '</ul>';
|
||||
question += '<b>{{ lang._('Weight: ') }}</b>';
|
||||
|
|
@ -458,12 +416,12 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
question,
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
var payload = {
|
||||
'server_ids': server_ids,
|
||||
'weight': $("#newBulkWeight").val()
|
||||
const payload = {
|
||||
'server_ids': server_ids,
|
||||
'weight': $("#newBulkWeight").val()
|
||||
};
|
||||
|
||||
$.post('/api/haproxy/maintenance/server_weight_bulk', payload, function(data) {
|
||||
ajaxCall("/api/haproxy/maintenance/server_weight_bulk", payload, function(data, status) {
|
||||
if (data.status != 'ok') {
|
||||
BootstrapDialog.show({
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
|
|
@ -475,7 +433,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
dialog.close();
|
||||
// reload - because some are successfully executed
|
||||
$("#grid-status").bootgrid("reload");
|
||||
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{#
|
||||
|
||||
Copyright (C) 2016 Frank Wall
|
||||
Copyright (C) 2016-2026 Frank Wall
|
||||
OPNsense® is Copyright © 2014 – 2016 by Deciso B.V.
|
||||
All rights reserved.
|
||||
|
||||
|
|
@ -29,7 +29,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
var gridopt = {
|
||||
'use strict';
|
||||
|
||||
const gridopt = {
|
||||
ajax: false,
|
||||
selection: false,
|
||||
multiSelect: false
|
||||
|
|
@ -37,118 +39,96 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
$("#grid-status").bootgrid('destroy');
|
||||
$("#grid-status").bootgrid(gridopt);
|
||||
|
||||
// build table rows safely from key/value data
|
||||
function buildInfoRows(data) {
|
||||
return Object.entries(data).map(function([key, value]) {
|
||||
return $("<tr/>").append(
|
||||
$("<td/>").text(key),
|
||||
$("<td/>").text(value)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// build table rows from an array of objects using a field list
|
||||
function buildGridRows(data, fields) {
|
||||
return Object.values(data).map(function(value) {
|
||||
const $tr = $("<tr/>");
|
||||
fields.forEach(function(field) {
|
||||
$("<td/>").text(value[field] != null ? value[field] : '').appendTo($tr);
|
||||
});
|
||||
return $tr;
|
||||
});
|
||||
}
|
||||
|
||||
// update info
|
||||
$("#update-info").click(function() {
|
||||
$('#processing-dialog').modal('show');
|
||||
$('#updatelist').empty();
|
||||
ajaxGet(url = "/api/haproxy/statistics/info/", sendData={},
|
||||
callback = function (data, status) {
|
||||
$("#infolist > tbody").empty();
|
||||
$("#infolist > thead").hide();
|
||||
if (status == "success") {
|
||||
$("#infolist > thead").show();
|
||||
$.each(data, function (key, value) {
|
||||
$('#infolist > tbody').append('<tr><td>'+key+'</td>' +
|
||||
"<td>"+value+"</td></tr>");
|
||||
});
|
||||
} else {
|
||||
$("#infolist > tbody").append("<tr><td colspan=2 style='text-align:center;'><br/>{{ lang._('The statistics could not be fetched. Is HAProxy running?') }}<br/><br/></td></tr>");
|
||||
}
|
||||
$('#processing-dialog').modal('hide');
|
||||
ajaxGet("/api/haproxy/statistics/info/", {},
|
||||
function (data, status) {
|
||||
$("#infolist > tbody").empty();
|
||||
$("#infolist > thead").hide();
|
||||
if (status == "success") {
|
||||
$("#infolist > thead").show();
|
||||
$("#infolist > tbody").append(buildInfoRows(data));
|
||||
} else {
|
||||
$("<tr/>").append(
|
||||
$("<td/>").attr("colspan", 2).css("text-align", "center")
|
||||
.html("<br/>{{ lang._('The statistics could not be fetched. Is HAProxy running?') }}<br/><br/>")
|
||||
).appendTo("#infolist > tbody");
|
||||
}
|
||||
$('#processing-dialog').modal('hide');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// update status
|
||||
$("#update-status").click(function() {
|
||||
$('#processing-dialog').modal('show');
|
||||
ajaxGet(url = "/api/haproxy/statistics/counters/", sendData={},
|
||||
callback = function (data, status) {
|
||||
if (status == "success") {
|
||||
// status
|
||||
$("#status_nav").show();
|
||||
$("#grid-status").bootgrid('destroy');
|
||||
var html = [];
|
||||
$.each(data, function (key, value) {
|
||||
var fields = ["id", "pxname", "svname", "status", "lastchg", "weight", "act", "downtime"];
|
||||
tr_str = '<tr>';
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
if (value[fields[i]] != null) {
|
||||
tr_str += '<td>' + value[fields[i]] + '</td>';
|
||||
} else {
|
||||
tr_str += '<td></td>';
|
||||
}
|
||||
}
|
||||
tr_str += '</tr>';
|
||||
html.push(tr_str);
|
||||
});
|
||||
$("#grid-status > tbody").html(html.join(''));
|
||||
$("#grid-status").bootgrid(gridopt);
|
||||
}
|
||||
$('#processing-dialog').modal('hide');
|
||||
ajaxGet("/api/haproxy/statistics/counters/", {},
|
||||
function (data, status) {
|
||||
if (status == "success") {
|
||||
const fields = ["id", "pxname", "svname", "status", "lastchg", "weight", "act", "downtime"];
|
||||
$("#status_nav").show();
|
||||
$("#grid-status").bootgrid('destroy');
|
||||
$("#grid-status > tbody").empty().append(buildGridRows(data, fields));
|
||||
$("#grid-status").bootgrid(gridopt);
|
||||
}
|
||||
$('#processing-dialog').modal('hide');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// update counters
|
||||
$("#update-counters").click(function() {
|
||||
$('#processing-dialog').modal('show');
|
||||
ajaxGet(url = "/api/haproxy/statistics/counters/", sendData={},
|
||||
callback = function (data, status) {
|
||||
if (status == "success") {
|
||||
// counters
|
||||
$("#counters_nav").show();
|
||||
$("#grid-counters").bootgrid('destroy');
|
||||
var html = [];
|
||||
$.each(data, function (key, value) {
|
||||
var fields = ["id", "pxname", "svname", "qcur", "qmax", "qlimit", "rate", "rate_max", "rate_lim", "scur", "smax", "slim", "stot", "bin", "bout", "dreq", "dresp", "ereq", "econ", "eresp", "wretr", "wredis"];
|
||||
tr_str = '<tr>';
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
if (value[fields[i]] != null) {
|
||||
tr_str += '<td>' + value[fields[i]] + '</td>';
|
||||
} else {
|
||||
tr_str += '<td></td>';
|
||||
}
|
||||
}
|
||||
tr_str += '</tr>';
|
||||
html.push(tr_str);
|
||||
});
|
||||
$("#grid-counters> tbody").html(html.join(''));
|
||||
$("#grid-counters").bootgrid(gridopt);
|
||||
}
|
||||
$('#processing-dialog').modal('hide');
|
||||
ajaxGet("/api/haproxy/statistics/counters/", {},
|
||||
function (data, status) {
|
||||
if (status == "success") {
|
||||
const fields = ["id", "pxname", "svname", "qcur", "qmax", "qlimit", "rate", "rate_max", "rate_lim", "scur", "smax", "slim", "stot", "bin", "bout", "dreq", "dresp", "ereq", "econ", "eresp", "wretr", "wredis"];
|
||||
$("#counters_nav").show();
|
||||
$("#grid-counters").bootgrid('destroy');
|
||||
$("#grid-counters > tbody").empty().append(buildGridRows(data, fields));
|
||||
$("#grid-counters").bootgrid(gridopt);
|
||||
}
|
||||
$('#processing-dialog').modal('hide');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// update tables
|
||||
$("#update-tables").click(function() {
|
||||
$('#processing-dialog').modal('show');
|
||||
ajaxGet(url = "/api/haproxy/statistics/tables/", sendData={},
|
||||
callback = function (data, status) {
|
||||
if (status == "success") {
|
||||
// tables
|
||||
$("#tables_nav").show();
|
||||
$("#grid-tables").bootgrid('destroy');
|
||||
var html = [];
|
||||
$.each(data, function (key, value) {
|
||||
var fields = ["table", "type", "size", "used"];
|
||||
tr_str = '<tr>';
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
if (value[fields[i]] != null) {
|
||||
tr_str += '<td>' + value[fields[i]] + '</td>';
|
||||
} else {
|
||||
tr_str += '<td></td>';
|
||||
}
|
||||
}
|
||||
tr_str += '</tr>';
|
||||
html.push(tr_str);
|
||||
});
|
||||
$("#grid-tables> tbody").html(html.join(''));
|
||||
$("#grid-tables").bootgrid(gridopt);
|
||||
}
|
||||
$('#processing-dialog').modal('hide');
|
||||
ajaxGet("/api/haproxy/statistics/tables/", {},
|
||||
function (data, status) {
|
||||
if (status == "success") {
|
||||
const fields = ["table", "type", "size", "used"];
|
||||
$("#tables_nav").show();
|
||||
$("#grid-tables").bootgrid('destroy');
|
||||
$("#grid-tables > tbody").empty().append(buildGridRows(data, fields));
|
||||
$("#grid-tables").bootgrid(gridopt);
|
||||
}
|
||||
$('#processing-dialog').modal('hide');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016-2018 Frank Wall
|
||||
* Copyright (C) 2016-2026 Frank Wall
|
||||
* Copyright (C) 2015 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
|
|
@ -41,12 +41,56 @@ if (isset($configObj->OPNsense->HAProxy->mapfiles)) {
|
|||
foreach ($configObj->OPNsense->HAProxy->mapfiles->children() as $mapfile) {
|
||||
$mf_name = (string)$mapfile->name;
|
||||
$mf_id = (string)$mapfile->id;
|
||||
$mf_url = (string)$mapfile->url;
|
||||
if ($mf_id != "") {
|
||||
$mf_content = htmlspecialchars_decode(str_replace("\r", "", (string)$mapfile->content));
|
||||
$mf_filename = $export_path . $mf_id . ".txt";
|
||||
file_put_contents($mf_filename, $mf_content);
|
||||
chmod($mf_filename, 0600);
|
||||
echo "map file exported to " . $mf_filename . "\n";
|
||||
// Download file from URL (if URL was provided).
|
||||
try {
|
||||
if ($mf_url == "") {
|
||||
throw new \Exception("no URL provided");
|
||||
}
|
||||
$fp = fopen($mf_filename, 'wb');
|
||||
if ($fp === false) {
|
||||
throw new \Exception("unable to open {$mf_filename} for writing");
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $mf_url);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
|
||||
curl_setopt($ch, CURLOPT_FAILONERROR, true);
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
|
||||
if (!curl_exec($ch)) {
|
||||
throw new \Exception("download error: " . curl_error($ch));
|
||||
}
|
||||
echo "map file downloaded to " . $mf_filename . "\n";
|
||||
} catch (\Exception $e) {
|
||||
// Show error message only if URL was specified.
|
||||
if ($mf_url != "") {
|
||||
echo "download of map file failed, error: " . $e->getMessage() . "\n";
|
||||
echo "trying to fill map file with fallback content\n";
|
||||
$mf_content = "# NOTE: Download failed, this is the fallback content.\n";
|
||||
} else {
|
||||
$mf_content = '';
|
||||
}
|
||||
|
||||
// Write contents to map file.
|
||||
// This is also used as a fallback if map file download fails.
|
||||
$mf_content = $mf_content . htmlspecialchars_decode(str_replace("\r", "", (string)$mapfile->content));
|
||||
file_put_contents($mf_filename, $mf_content);
|
||||
echo "map file exported to " . $mf_filename . "\n";
|
||||
} finally {
|
||||
if (isset($ch)) {
|
||||
curl_close($ch);
|
||||
}
|
||||
if (isset($fp) && is_resource($fp)) {
|
||||
fclose($fp);
|
||||
}
|
||||
chmod($mf_filename, 0600);
|
||||
chown($mf_filename, 'www');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,59 +171,44 @@
|
|||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'hdr' %}
|
||||
{% do acl_options.append('hdr(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.hdr|default("") != "" %}
|
||||
{% do acl_options.append('hdr(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.hdr) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'hdr_beg' %}
|
||||
{% do acl_options.append('hdr_beg(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.hdr_beg|default("") != "" %}
|
||||
{% do acl_options.append('hdr_beg(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.hdr_beg) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'hdr_end' %}
|
||||
{% do acl_options.append('hdr_end(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.hdr_end|default("") != "" %}
|
||||
{% do acl_options.append('hdr_end(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.hdr_end) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'hdr_reg' %}
|
||||
{% do acl_options.append('hdr_reg(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.hdr_reg|default("") != "" %}
|
||||
{% do acl_options.append('hdr_reg(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.hdr_reg) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'hdr_sub' %}
|
||||
{% do acl_options.append('hdr_sub(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.hdr_sub|default("") != "" %}
|
||||
{% do acl_options.append('hdr_sub(host)') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.hdr_sub) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'http_auth' %}
|
||||
{% if acl_data.allowedUsers|default("") != "" or acl_data.allowedGroups|default("") != "" %}
|
||||
|
|
@ -253,70 +238,52 @@
|
|||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'path' %}
|
||||
{% do acl_options.append('path') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.path|default("") != "" %}
|
||||
{% do acl_options.append('path') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.path) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'path_beg' %}
|
||||
{% do acl_options.append('path_beg') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.path_beg|default("") != "" %}
|
||||
{% do acl_options.append('path_beg') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.path_beg) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'path_dir' %}
|
||||
{% do acl_options.append('path_dir') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.path_dur|default("") != "" %}
|
||||
{% do acl_options.append('path_dir') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.path_dir) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'path_end' %}
|
||||
{% do acl_options.append('path_end') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.path_end|default("") != "" %}
|
||||
{% do acl_options.append('path_end') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.path_end) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'path_reg' %}
|
||||
{% do acl_options.append('path_reg') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.path_reg|default("") != "" %}
|
||||
{% do acl_options.append('path_reg') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.path_reg) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'path_sub' %}
|
||||
{% do acl_options.append('path_sub') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% if acl_data.path_sub|default("") != "" %}
|
||||
{% do acl_options.append('path_sub') %}
|
||||
{% if acl_data.caseSensitive|default('0') == '0' %}
|
||||
{% do acl_options.append('-i') %}
|
||||
{% endif %}
|
||||
{% do acl_options.append(acl_data.path_sub) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'sc_bytes_in_rate' %}
|
||||
{% if acl_data.sc_number|default("") != "" and acl_data.sc_bytes_in_rate|default("") != "" %}
|
||||
|
|
@ -481,7 +448,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_conn_cnt(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_conn_cnt_comparison ~ ' ' ~ acl_data.sc_conn_cnt) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_conn_cnt(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_conn_cnt_comparison ~ ' ' ~ acl_data.sc_conn_cnt) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -493,7 +465,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_conn_cur(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_conn_cur_comparison ~ ' ' ~ acl_data.sc_conn_cur) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_conn_cur(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_conn_cur_comparison ~ ' ' ~ acl_data.sc_conn_cur) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -505,7 +482,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_conn_rate(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_conn_rate_comparison ~ ' ' ~ acl_data.sc_conn_rate) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_conn_rate(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_conn_rate_comparison ~ ' ' ~ acl_data.sc_conn_rate) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -817,7 +799,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_err_cnt(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_http_err_cnt_comparison ~ ' ' ~ acl_data.sc_http_err_cnt) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_err_cnt(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_http_err_cnt_comparison ~ ' ' ~ acl_data.sc_http_err_cnt) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -829,7 +816,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_err_rate(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_http_err_rate_comparison ~ ' ' ~ acl_data.sc_http_err_rate) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_err_rate(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_http_err_rate_comparison ~ ' ' ~ acl_data.sc_http_err_rate) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -841,7 +833,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_fail_cnt(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_http_fail_cnt_comparison ~ ' ' ~ acl_data.sc_http_fail_cnt) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_fail_cnt(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_http_fail_cnt_comparison ~ ' ' ~ acl_data.sc_http_fail_cnt) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -853,7 +850,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_fail_rate(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_http_fail_rate_comparison ~ ' ' ~ acl_data.sc_http_fail_rate) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_fail_rate(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_http_fail_rate_comparison ~ ' ' ~ acl_data.sc_http_fail_rate) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -865,7 +867,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_req_cnt(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_http_req_cnt_comparison ~ ' ' ~ acl_data.sc_http_req_cnt) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_req_cnt(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_http_req_cnt_comparison ~ ' ' ~ acl_data.sc_http_req_cnt) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -877,7 +884,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_req_rate(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_http_req_rate_comparison ~ ' ' ~ acl_data.sc_http_req_rate) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_http_req_rate(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_http_req_rate_comparison ~ ' ' ~ acl_data.sc_http_req_rate) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -997,7 +1009,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_sess_cnt(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_sess_cnt_comparison ~ ' ' ~ acl_data.sc_sess_cnt) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_sess_cnt(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_sess_cnt_comparison ~ ' ' ~ acl_data.sc_sess_cnt) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -1009,7 +1026,12 @@
|
|||
{% else %}
|
||||
{% set table_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_sess_rate(' ~ acl_data.sc_number ~ table_data ~ ') ' ~ acl_data.sc_sess_rate_comparison ~ ' ' ~ acl_data.sc_sess_rate) %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('sc_sess_rate(' ~ acl_data.sc_number ~ table_data ~ ')' ~ converter_data ~ ' ' ~ acl_data.sc_sess_rate_comparison ~ ' ' ~ acl_data.sc_sess_rate) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
|
|
@ -1414,6 +1436,18 @@
|
|||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{% elif acl_data.expression == 'var' %}
|
||||
{% if acl_data.var|default("") != "" and acl_data.var_value|default("") != "" %}
|
||||
{% if acl_data.converter|default("") != "" %}
|
||||
{% set converter_data = ',' ~ acl_data.converter %}
|
||||
{% else %}
|
||||
{% set converter_data = '' %}
|
||||
{% endif %}
|
||||
{% do acl_options.append('var' ~ acl_data.var ~ converter_data ~ ' ' ~ acl_data.var_comparison ~ ' ' ~ acl_data.var_value) %}
|
||||
{% else %}
|
||||
{% set acl_enabled = '0' %}
|
||||
# ERROR: missing parameters
|
||||
{% endif %}
|
||||
{# # handle boolean ACL types that do not require any input #}
|
||||
{% elif acl_data.expression in acl_boolean_types %}
|
||||
{% do acl_options.append(acl_data.expression) %}
|
||||
|
|
@ -1520,6 +1554,8 @@
|
|||
{% set action_keyword_data = 'lua.' ~ action_data.http_request_option %}
|
||||
{% elif action_data.http_request_action == 'set-var' %}
|
||||
{% set action_keyword_data = 'set-var' ~ action_data.http_request_option %}
|
||||
{% elif action_data.http_request_action == 'set-var-fmt' %}
|
||||
{% set action_keyword_data = 'set-var-fmt' ~ action_data.http_request_option %}
|
||||
{% elif action_data.http_request_action == 'use-service' %}
|
||||
{% set action_keyword_data = 'use-service lua.' ~ action_data.http_request_option %}
|
||||
{% else %}
|
||||
|
|
@ -1670,6 +1706,22 @@
|
|||
{% set action_enabled = '0' %}
|
||||
{% do global_action_options.append('# ERROR: unsupported rule type ' ~ action_data.type) %}
|
||||
{% endif %}
|
||||
{# # Add sample fetch to map file config. #}
|
||||
{% if action_data.mapfile|default("") != "" %}
|
||||
{% set mapfile_data = helpers.getUUID(action_data.mapfile) %}
|
||||
{% set mapfile_path = '/tmp/haproxy/mapfiles/' ~ mapfile_data.id ~ '.txt' %}
|
||||
{% set mapfile_config = 'map_' ~ mapfile_data.type %}
|
||||
{% if action_data.map_default|default("") != "" %}
|
||||
{% set mapfile_default = ',' ~ action_data.map_default %}
|
||||
{% endif %}
|
||||
{% if action_data.sample_fetch|default("") != "" %}
|
||||
{% set mapfile_sf = action_data.sample_fetch ~ ',' %}
|
||||
{% endif %}
|
||||
{% do action_options.append(mapfile_sf ~ mapfile_config ~ '(' ~ mapfile_path ~ mapfile_default ~ ')') %}
|
||||
{# # Add/append sample fetch. #}
|
||||
{% elif action_data.sample_fetch|default("") != "" %}
|
||||
{% do action_options.append(action_data.sample_fetch) %}
|
||||
{% endif %}
|
||||
{# # Is this rule enabled in the GUI? #}
|
||||
{% if action_data.enabled|default('') == '1' %}
|
||||
{# # check if action is valid #}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
PLUGIN_NAME= isc-dhcp
|
||||
PLUGIN_VERSION= 1.0
|
||||
PLUGIN_REVISION= 3
|
||||
PLUGIN_REVISION= 4
|
||||
PLUGIN_COMMENT= ISC DHCPv4/v6 server
|
||||
PLUGIN_DEPENDS= isc-dhcp44-server
|
||||
PLUGIN_MAINTAINER= franco@opnsense.org
|
||||
|
|
|
|||
|
|
@ -1325,3 +1325,21 @@ function dhcpd_staticarp($interface, $ifconfig_details)
|
|||
interfaces_neighbors_configure($interface, $ifconfig_details);
|
||||
}
|
||||
}
|
||||
|
||||
function dhcpd_ip_in_interface_alias_subnet($interface, $ipalias)
|
||||
{
|
||||
if (empty($interface) || !is_ipaddr($ipalias)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (config_read_array('virtualip', 'vip', false) as $vip) {
|
||||
if ($vip['mode'] == 'ipalias' && $vip['interface'] == $interface) {
|
||||
$subnet = is_ipaddrv6($ipalias) ? gen_subnetv6($vip['subnet'], $vip['subnet_bits']) : gen_subnet($vip['subnet'], $vip['subnet_bits']);
|
||||
if (ip_in_subnet($ipalias, "{$subnet}/{$vip['subnet_bits']}")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace OPNsense\Interfaces\Neighbor;
|
||||
|
||||
use OPNsense\Core\Config;
|
||||
|
||||
class dhcpd
|
||||
{
|
||||
public function collect()
|
||||
{
|
||||
$result = [];
|
||||
$intfmap = [];
|
||||
$config = Config::getInstance()->object();
|
||||
if ($config->dhcpd->count() > 0) {
|
||||
foreach ($config->dhcpd->children() as $intf => $node) {
|
||||
foreach ($node->children() as $key => $data) {
|
||||
if ($key == 'staticmap') {
|
||||
if (!empty($data->arp_table_static_entry) || !empty($node->staticarp)) {
|
||||
$result[] = [
|
||||
'etheraddr' => (string)$data->mac,
|
||||
'ipaddress' => (string)$data->ipaddr,
|
||||
'descr' => (string)$data->descr,
|
||||
'source' => sprintf('dhcpd-%s', $intf)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -175,7 +175,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
|||
}
|
||||
list (, $parent_net) = interfaces_primary_address($pconfig['if']);
|
||||
if (is_subnetv4($parent_net) && $pconfig['gateway'] && $pconfig['gateway'] != "none") {
|
||||
if (!ip_in_subnet($pconfig['gateway'], $parent_net) && !ip_in_interface_alias_subnet($pconfig['if'], $pconfig['gateway'])) {
|
||||
if (!ip_in_subnet($pconfig['gateway'], $parent_net) && !dhcpd_ip_in_interface_alias_subnet($pconfig['if'], $pconfig['gateway'])) {
|
||||
$input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $pconfig['gateway']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
|||
}
|
||||
|
||||
if (is_subnetv4($parent_net) && $pconfig['gateway'] != "none" && !empty($pconfig['gateway'])) {
|
||||
if (!ip_in_subnet($pconfig['gateway'], $parent_net) && !ip_in_interface_alias_subnet($if, $pconfig['gateway'])) {
|
||||
if (!ip_in_subnet($pconfig['gateway'], $parent_net) && !dhcpd_ip_in_interface_alias_subnet($if, $pconfig['gateway'])) {
|
||||
$input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet."), $_POST['gateway']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_NAME= tayga
|
||||
PLUGIN_VERSION= 1.4
|
||||
PLUGIN_VERSION= 1.5
|
||||
PLUGIN_COMMENT= Tayga NAT64
|
||||
PLUGIN_DEPENDS= tayga
|
||||
PLUGIN_MAINTAINER= m.muenz@gmail.com
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ networks where dedicated NAT64 hardware would be overkill.
|
|||
Plugin Changelog
|
||||
================
|
||||
|
||||
1.5
|
||||
|
||||
* Allow non-global IPv4 addresses when using 64:ff9b::/96 (contributed by Maurice Walker)
|
||||
|
||||
1.4
|
||||
|
||||
* Enable forwarding of UDP packets with zero checksum (contributed by Maurice Walker)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
tun-device nat64
|
||||
data-dir /var/db/tayga
|
||||
udp-cksum-mode fwd
|
||||
wkpf-strict no
|
||||
|
||||
ipv4-addr {{ OPNsense.tayga.general.v4address }}
|
||||
{% if helpers.exists('OPNsense.tayga.general.v6address') and OPNsense.tayga.general.v6address != '' %}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_NAME= upnp
|
||||
PLUGIN_VERSION= 1.8
|
||||
PLUGIN_VERSION= 1.9
|
||||
PLUGIN_DEPENDS= miniupnpd
|
||||
PLUGIN_COMMENT= UPnP IGD & PCP/NAT-PMP Service
|
||||
PLUGIN_MAINTAINER= franco@opnsense.org
|
||||
|
|
|
|||
|
|
@ -7,6 +7,24 @@ WWW: https://miniupnp.tuxfamily.org/
|
|||
Plugin Changelog
|
||||
================
|
||||
|
||||
1.9
|
||||
|
||||
* Separate service log file and log level UI option
|
||||
* More specific allow third-party mapping UI option
|
||||
* Impove help/wording and update missed changelog
|
||||
* Add daemon patch to improve logging
|
||||
|
||||
(all contributed by Self-Hosting-Group)
|
||||
|
||||
1.8
|
||||
|
||||
* New UI options: disable IPv6 mapping, allow third-party mapping, UPnP IGD compatibility, router/friendly name; remove option: report system uptime (bug)
|
||||
* List IPv6 maps and keep active maps when reconfiguring/restarting service, clearer added via / description field
|
||||
* New UI sections, rewording plugin, set allow-filtered with STUN to workaround CGNAT test limitation, clean up daemon config
|
||||
* Update daemon to 2.3.9, add build options (e.g. IGDv2 support), add daemon patch to improve UPnP IGDv2 compatibility
|
||||
|
||||
(all contributed by Self-Hosting-Group)
|
||||
|
||||
1.7
|
||||
|
||||
* Add option to allow arbitrary number of UPnP/NAT-PMP rules (contributed by Kreeblah)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@
|
|||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
function miniupnpd_syslog()
|
||||
{
|
||||
return ['miniupnpd' => ['facility' => ['miniupnpd']]];
|
||||
}
|
||||
|
||||
function miniupnpd_enabled()
|
||||
{
|
||||
global $config;
|
||||
|
|
@ -39,10 +44,12 @@ function miniupnpd_firewall($fw)
|
|||
return;
|
||||
}
|
||||
|
||||
/* required for IPv4: */
|
||||
$fw->registerAnchor('miniupnpd', 'rdr');
|
||||
/* required for IPv6: */
|
||||
$fw->registerAnchor('miniupnpd', 'fw');
|
||||
/* required for IPv4 NAT hairpinning: */
|
||||
$fw->registerAnchor('miniupnpd', 'nat', 0, 'head');
|
||||
$fw->registerAnchor('miniupnpd', 'binat');
|
||||
}
|
||||
|
||||
function miniupnpd_services()
|
||||
|
|
@ -67,6 +74,8 @@ function miniupnpd_services()
|
|||
|
||||
function miniupnpd_start()
|
||||
{
|
||||
global $config;
|
||||
|
||||
if (!miniupnpd_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -75,15 +84,29 @@ function miniupnpd_start()
|
|||
return;
|
||||
}
|
||||
|
||||
mwexecfb('/usr/local/sbin/miniupnpd -f %s -P %s', [ '/var/etc/miniupnpd.conf', '/var/run/miniupnpd.pid']);
|
||||
$cmd_frmt = ['/usr/local/sbin/miniupnpd -f %s -P %s'];
|
||||
$cmd_args = ['/var/etc/miniupnpd.conf', '/var/run/miniupnpd.pid'];
|
||||
|
||||
switch ($config['installedpackages']['miniupnpd']['config'][0]['log_level'] ?? '') {
|
||||
case 'debug':
|
||||
$cmd_frmt[] = '-vv';
|
||||
break;
|
||||
case 'info':
|
||||
$cmd_frmt[] = '-v';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mwexecfb($cmd_frmt, $cmd_args);
|
||||
}
|
||||
|
||||
function miniupnpd_stop()
|
||||
{
|
||||
killbypid('/var/run/miniupnpd.pid');
|
||||
|
||||
mwexecf('/sbin/pfctl -a miniupnpd -Fr');
|
||||
mwexecf('/sbin/pfctl -a miniupnpd -Fn');
|
||||
mwexecf('/sbin/pfctl -a miniupnpd -F rules');
|
||||
mwexecf('/sbin/pfctl -a miniupnpd -F nat');
|
||||
}
|
||||
|
||||
function miniupnpd_configure()
|
||||
|
|
@ -198,11 +221,15 @@ function miniupnpd_configure_do($verbose = false)
|
|||
$config_text .= "bitrate_up={$upload}\n";
|
||||
}
|
||||
|
||||
if (!empty($upnp_config['allow_third_party_mapping'])) {
|
||||
if (in_array($upnp_config['allow_third_party_mapping'] ?? '', ['1', 'upnp-igd'])) {
|
||||
$config_text .= "secure_mode=no\n";
|
||||
$config_text .= "pcp_allow_thirdparty=yes\n";
|
||||
} else {
|
||||
$config_text .= "secure_mode=yes\n";
|
||||
}
|
||||
|
||||
if (in_array($upnp_config['allow_third_party_mapping'] ?? '', ['1', 'pcp'])) {
|
||||
$config_text .= "pcp_allow_thirdparty=yes\n";
|
||||
} else {
|
||||
$config_text .= "pcp_allow_thirdparty=no\n";
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +244,8 @@ function miniupnpd_configure_do($verbose = false)
|
|||
|
||||
/* enable system uptime instead of miniupnpd uptime */
|
||||
if (!empty($upnp_config['sysuptime'])) {
|
||||
$config_text .= "system_uptime=yes\n";
|
||||
/* Disable system uptime to workaround daemon bug with PCP/NAT-PMP epoch on BSD */
|
||||
//$config_text .= "system_uptime=yes\n";
|
||||
}
|
||||
|
||||
/* set webgui url */
|
||||
|
|
|
|||
|
|
@ -11,4 +11,11 @@
|
|||
<pattern>status_upnp.php*</pattern>
|
||||
</patterns>
|
||||
</page-status-upnpstatus>
|
||||
<page-diagnostics-logs-upnp>
|
||||
<name>Services: UPnP IGD & PCP: Log File</name>
|
||||
<patterns>
|
||||
<pattern>ui/diagnostics/log/core/miniupnpd/*</pattern>
|
||||
<pattern>api/diagnostics/log/core/miniupnpd/*</pattern>
|
||||
</patterns>
|
||||
</page-diagnostics-logs-upnp>
|
||||
</acl>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
<Edit url="/services_upnp.php?*" visibility="hidden"/>
|
||||
</Settings>
|
||||
<ActiveMaps VisibleName="Active Maps" order="20" url="/status_upnp.php"/>
|
||||
<LogFile VisibleName="Log File" order="30" url="/ui/diagnostics/log/core/miniupnpd"/>
|
||||
</UPnP>
|
||||
</Services>
|
||||
</menu>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
###################################################################
|
||||
# Local syslog-ng configuration filter definition [miniupnpd].
|
||||
###################################################################
|
||||
filter f_local_miniupnpd {
|
||||
program("miniupnpd");
|
||||
};
|
||||
|
|
@ -78,6 +78,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
|||
'friendly_name',
|
||||
'iface_array',
|
||||
'ipv6_disable',
|
||||
'log_level',
|
||||
'logpackets',
|
||||
'overridesubnet',
|
||||
'overridewanip',
|
||||
|
|
@ -178,7 +179,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
|||
// save form data
|
||||
$upnp = [];
|
||||
// boolean types
|
||||
foreach (['enable', 'enable_upnp', 'enable_natpmp', 'logpackets', 'sysuptime', 'permdefault', 'allow_third_party_mapping', 'ipv6_disable'] as $fieldname) {
|
||||
foreach (['enable', 'enable_upnp', 'enable_natpmp', 'logpackets', 'sysuptime', 'permdefault', 'ipv6_disable'] as $fieldname) {
|
||||
$upnp[$fieldname] = !empty($pconfig[$fieldname]);
|
||||
}
|
||||
// numeric types
|
||||
|
|
@ -186,7 +187,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
|||
$upnp['num_permuser'] = $pconfig['num_permuser'];
|
||||
}
|
||||
// text field types
|
||||
foreach (['ext_iface', 'download', 'upload', 'overridewanip', 'overridesubnet', 'stun_host', 'stun_port', 'friendly_name', 'upnp_igd_compat'] as $fieldname) {
|
||||
foreach (['allow_third_party_mapping', 'download', 'ext_iface', 'friendly_name', 'log_level', 'overridesubnet', 'overridewanip', 'stun_host', 'stun_port', 'upload', 'upnp_igd_compat'] as $fieldname) {
|
||||
$upnp[$fieldname] = $pconfig[$fieldname];
|
||||
}
|
||||
foreach (miniupnpd_permuser_list() as $fieldname) {
|
||||
|
|
@ -233,16 +234,16 @@ include("head.inc");
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a id="help_for_enable" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Enable");?></td>
|
||||
<td><a id="help_for_enable" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Enabled");?></td>
|
||||
<td>
|
||||
<input name="enable" type="checkbox" value="yes" <?=!empty($pconfig['enable']) ? "checked=\"checked\"" : ""; ?> />
|
||||
<div class="hidden" data-for="help_for_enable">
|
||||
<?=gettext("Start the autonomous port mapping service.");?>
|
||||
<?=gettext("Enable the autonomous port mapping service.");?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_enable_upnp" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Allow UPnP IGD Port Mapping");?></td>
|
||||
<td><a id="help_for_enable_upnp" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Enable UPnP IGD protocol");?></td>
|
||||
<td>
|
||||
<input name="enable_upnp" type="checkbox" value="yes" <?=!empty($pconfig['enable_upnp']) ? "checked=\"checked\"" : ""; ?> />
|
||||
<div class="hidden" data-for="help_for_enable_upnp">
|
||||
|
|
@ -251,7 +252,7 @@ include("head.inc");
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_enable_natpmp" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Allow PCP/NAT-PMP Port Mapping");?></td>
|
||||
<td><a id="help_for_enable_natpmp" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Enable PCP/NAT-PMP protocols");?></td>
|
||||
<td>
|
||||
<input name="enable_natpmp" type="checkbox" value="yes" <?=!empty($pconfig['enable_natpmp']) ? "checked=\"checked\"" : ""; ?> />
|
||||
<div class="hidden" data-for="help_for_enable_natpmp">
|
||||
|
|
@ -324,16 +325,19 @@ include("head.inc");
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Override external IPv4");?></td>
|
||||
<td><a id="help_for_overridewanip" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Override external IPv4");?></td>
|
||||
<td>
|
||||
<input name="overridewanip" type="text" value="<?=$pconfig['overridewanip'];?>" />
|
||||
<div class="hidden" data-for="help_for_overridewanip">
|
||||
<?=gettext('Report custom public/external (WAN) IPv4 address.');?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_overridesubnet" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Internal interface IPv4 subnet override");?></td>
|
||||
<td>
|
||||
<select name="overridesubnet" class="selectpicker" id="overridesubnet">
|
||||
<option value="" <?= empty($pconfig['overridesubnet']) ? 'selected="selected"' : '' ?>><?= gettext('default') ?></option>
|
||||
<option value="" <?= empty($pconfig['overridesubnet']) ? 'selected="selected"' : '' ?>><?= gettext('Default') ?></option>
|
||||
<?php for ($i = 32; $i >= 1; $i--): ?>
|
||||
<option value="<?= $i ?>" <?=!empty($pconfig['overridesubnet']) && $pconfig['overridesubnet'] == $i ? 'selected="selected"' : '' ?>><?= $i ?></option>
|
||||
<?php endfor ?>
|
||||
|
|
@ -346,9 +350,14 @@ include("head.inc");
|
|||
<tr>
|
||||
<td><a id="help_for_allow_third_party_mapping" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Allow third-party mapping");?></td>
|
||||
<td>
|
||||
<input name="allow_third_party_mapping" type="checkbox" value="yes" <?=!empty($pconfig['allow_third_party_mapping']) ? "checked=\"checked\"" : ""; ?> />
|
||||
<select name="allow_third_party_mapping">
|
||||
<option value="0" <?= ($pconfig['allow_third_party_mapping'] ?? '') == '0' ? 'selected="selected"' : '' ?> ><?= gettext('Disabled (recommended)') ?></option>
|
||||
<option value="1" <?= ($pconfig['allow_third_party_mapping'] ?? '') == '1' ? 'selected="selected"' : '' ?> ><?= gettext('Enabled') ?></option>
|
||||
<option value="upnp-igd" <?= ($pconfig['allow_third_party_mapping'] ?? '') == 'upnp-igd' ? 'selected="selected"' : '' ?> ><?= gettext('Enabled (UPnP IGD only)') ?></option>
|
||||
<option value="pcp" <?= ($pconfig['allow_third_party_mapping'] ?? '') == 'pcp' ? 'selected="selected"' : '' ?> ><?= gettext('Enabled (PCP only)') ?></option>
|
||||
</select>
|
||||
<div class="hidden" data-for="help_for_allow_third_party_mapping">
|
||||
<?=gettext("Allow adding port maps for non-requesting IP addresses.");?>
|
||||
<?=gettext("Allow adding port maps for non-requesting IP addresses; use with care.");?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -367,6 +376,16 @@ include("head.inc");
|
|||
</div>
|
||||
</td>
|
||||
</tr> -->
|
||||
<tr>
|
||||
<td><i class="fa fa-info-circle text-muted"></i> <?= gettext('Log level') ?></td>
|
||||
<td>
|
||||
<select name="log_level">
|
||||
<option value="" <?= ($pconfig['log_level'] ?? '') == '' ? 'selected="selected"' : '' ?> ><?= gettext('Default') ?></option>
|
||||
<option value="info" <?= ($pconfig['log_level'] ?? '') == 'info' ? 'selected="selected"' : '' ?> ><?= gettext('Info') ?></option>
|
||||
<option value="debug" <?= ($pconfig['log_level'] ?? '') == 'debug' ? 'selected="selected"' : '' ?> ><?= gettext('Debug') ?></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_logpackets" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Firewall logs");?></td>
|
||||
<td>
|
||||
|
|
@ -393,12 +412,15 @@ include("head.inc");
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><i class="fa fa-info-circle text-muted"></i> <?= gettext('UPnP IGD compatibility mode') ?></td>
|
||||
<td><a id="help_for_upnp_igd_compat" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?= gettext('UPnP IGD compatibility') ?></td>
|
||||
<td>
|
||||
<select name="upnp_igd_compat">
|
||||
<option value="igdv1" <?= ($pconfig['upnp_igd_compat'] ?? '') == 'igdv1' ? 'selected="selected"' : '' ?> ><?= gettext('IGDv1 (IPv4 only)') ?></option>
|
||||
<option value="igdv2" <?= ($pconfig['upnp_igd_compat'] ?? '') == 'igdv2' ? 'selected="selected"' : '' ?> ><?= gettext('IGDv2 (with workarounds)') ?></option>
|
||||
</select>
|
||||
<div class="hidden" data-for="help_for_upnp_igd_compat">
|
||||
<?=sprintf(gettext('Set compatibility mode (act as device) to workaround IGDv2-incompatible clients; %s are known to only work with %s.'), 'Sony PS, Activision CoD…', 'IGDv1');?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
@ -436,7 +458,7 @@ include("head.inc");
|
|||
<table class="table table-striped opnsense_standard_table_form">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2"><?=gettext("Custom Access Control List");?></th>
|
||||
<th colspan="2"><?=gettext("Access Control List");?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ include("head.inc");
|
|||
<th><?=gettext("Port")?></th>
|
||||
<th><?=gettext("External port")?></th>
|
||||
<th><?=gettext("Protocol")?></th>
|
||||
<th><?=gettext("Source IP")?></th>
|
||||
<th><?=gettext("Source port")?></th>
|
||||
<th><?=gettext("Remote IP")?></th>
|
||||
<th><?=gettext("Remote port")?></th>
|
||||
<th><?=gettext("Added via / description")?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -83,8 +83,8 @@ include("head.inc");
|
|||
!preg_match('/on (?P<iface>.+) inet6 proto (?P<proto>.+) from (?P<srcaddr>[^ ]+) (port = (?P<srcport>.+) )?to (?P<intaddr>.+) port = (?P<intport>\d+) (flags [^ ]+ )?keep state (label "(?P<descr>.+)" )?rtable [0-9]/', $rdr_entry, $matches)) {
|
||||
continue;
|
||||
}
|
||||
if (preg_match('/PCP ([A-Z]+) ([0-9a-f]{24})$/', $matches['descr'], $descrmatch) === 1) {
|
||||
$descr = "PCP ({$descrmatch[1]} nonce {$descrmatch[2]})";
|
||||
if (preg_match('/PCP [A-Z]+ ([0-9a-f]{24})$/', $matches['descr'], $descrmatch) === 1) {
|
||||
$descr = "PCP (nonce {$descrmatch[1]})";
|
||||
} elseif (preg_match('/^NAT-PMP \d+ \w+$/', $matches['descr'], $descrmatch) === 1) {
|
||||
$descr = 'NAT-PMP';
|
||||
} elseif (preg_match('/^pinhole-(\d+).*IGD2 pinhole$/', $matches['descr'], $descrmatch) === 1) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
PLUGIN_NAME= wol
|
||||
PLUGIN_VERSION= 2.5
|
||||
PLUGIN_REVISION= 3
|
||||
PLUGIN_REVISION= 4
|
||||
PLUGIN_DEPENDS= wol
|
||||
PLUGIN_COMMENT= Wake on LAN Service
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
<patterns>
|
||||
<pattern>ui/wol/*</pattern>
|
||||
<pattern>api/wol/wol/*</pattern>
|
||||
<pattern>api/diagnostics/interface/get_arp*</pattern>
|
||||
</patterns>
|
||||
</page-services-wakeonlan>
|
||||
</acl>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_NAME= acme-client
|
||||
PLUGIN_VERSION= 4.13
|
||||
PLUGIN_VERSION= 4.15
|
||||
PLUGIN_COMMENT= ACME Client
|
||||
PLUGIN_MAINTAINER= opnsense@moov.de
|
||||
PLUGIN_DEPENDS= acme.sh py${PLUGIN_PYTHON}-dns-lexicon
|
||||
|
|
|
|||
|
|
@ -8,6 +8,21 @@ WWW: https://github.com/acmesh-official/acme.sh
|
|||
Plugin Changelog
|
||||
================
|
||||
|
||||
4.15
|
||||
|
||||
Added:
|
||||
* add support for deploy hook "truenas_ws" (#5309)
|
||||
|
||||
Changed:
|
||||
* always use configured cert name in cert description (#5282)
|
||||
|
||||
4.14
|
||||
|
||||
Fixed:
|
||||
* fix class name of Google Domains DNS API (to make PHP linter happy)
|
||||
* parameters for TrueNAS automation not visible (#5210)
|
||||
* multiple buttons not working (#5123)
|
||||
|
||||
4.13
|
||||
|
||||
Added:
|
||||
|
|
|
|||
|
|
@ -377,6 +377,47 @@
|
|||
<type>header</type>
|
||||
<style>method_table method_table_acme_truenas</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.acme_truenas_apikey</id>
|
||||
<label>TrueNAS API key</label>
|
||||
<type>text</type>
|
||||
<help>API key generated in the TrueNAS web UI.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.acme_truenas_hostname</id>
|
||||
<label>TrueNAS hostname</label>
|
||||
<type>text</type>
|
||||
<help>Hostname or IP address of TrueNAS Server.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.acme_truenas_scheme</id>
|
||||
<label>TrueNAS scheme</label>
|
||||
<type>dropdown</type>
|
||||
<help>Connection scheme that will be used when uploading certificates to TrueNAS Core Server.</help>
|
||||
</field>
|
||||
<field>
|
||||
<label>Required Parameters</label>
|
||||
<type>header</type>
|
||||
<style>method_table method_table_acme_truenasws</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.acme_truenasws_apikey</id>
|
||||
<label>TrueNAS API key</label>
|
||||
<type>text</type>
|
||||
<help>API key generated in the TrueNAS web UI.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.acme_truenasws_hostname</id>
|
||||
<label>TrueNAS hostname</label>
|
||||
<type>text</type>
|
||||
<help>Hostname or IP address of TrueNAS Server.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.acme_truenasws_protocol</id>
|
||||
<label>TrueNAS protocol</label>
|
||||
<type>dropdown</type>
|
||||
<help>Connection scheme that will be used when uploading certificates to TrueNAS Server.</help>
|
||||
</field>
|
||||
<field>
|
||||
<label>Required Parameters</label>
|
||||
<type>header</type>
|
||||
|
|
@ -399,24 +440,6 @@
|
|||
<label>Ruckus Password</label>
|
||||
<type>password</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.acme_truenas_apikey</id>
|
||||
<label>TrueNAS API key</label>
|
||||
<type>text</type>
|
||||
<help>API key generated in the TrueNAS web UI.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.acme_truenas_hostname</id>
|
||||
<label>TrueNAS hostname</label>
|
||||
<type>text</type>
|
||||
<help>Hostname or IP address of TrueNAS Core Server.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>action.acme_truenas_scheme</id>
|
||||
<label>TrueNAS scheme</label>
|
||||
<type>dropdown</type>
|
||||
<help>Connection scheme that will be used when uploading certificates to TrueNAS Core Server.</help>
|
||||
</field>
|
||||
<field>
|
||||
<label>Required Parameters</label>
|
||||
<type>header</type>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2026 Konstantinos Spartalis (cspartalis@potatonetworks.com)
|
||||
* Copyright (C) 2023 Jan Winkler
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace OPNsense\AcmeClient\LeAutomation;
|
||||
|
||||
use OPNsense\AcmeClient\LeAutomationInterface;
|
||||
|
||||
/**
|
||||
* Run acme.sh deploy hook truenas_ws
|
||||
* @package OPNsense\AcmeClient
|
||||
*/
|
||||
class AcmeTruenasWS extends Base implements LeAutomationInterface
|
||||
{
|
||||
public function prepare()
|
||||
{
|
||||
$this->acme_env['DEPLOY_TRUENAS_APIKEY'] = (string)$this->config->acme_truenasws_apikey;
|
||||
$this->acme_env['DEPLOY_TRUENAS_HOSTNAME'] = (string)$this->config->acme_truenasws_hostname;
|
||||
$this->acme_env['DEPLOY_TRUENAS_PROTOCOL'] = (string)$this->config->acme_truenasws_protocol;
|
||||
$this->acme_args[] = '--deploy-hook truenas_ws --insecure';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -203,7 +203,6 @@ class LeCertificate extends LeCommon
|
|||
$cert_details = CertStore::parseX509($cert_content);
|
||||
$cert_subject = $cert_details['name'];
|
||||
$cert_serial = $cert_details['serialNumber'];
|
||||
$cert_cn = $cert_details['commonname'];
|
||||
$cert_issuer = implode(",", $cert_details['issuer']);
|
||||
} else {
|
||||
LeUtils::log_error('unable to read certificate content from file');
|
||||
|
|
@ -226,12 +225,7 @@ class LeCertificate extends LeCommon
|
|||
$cert = array();
|
||||
$cert['refid'] = uniqid();
|
||||
$cert['caref'] = (string)$ca['refid'];
|
||||
if (empty($cert_cn)) {
|
||||
// Fallback to configured name if Common Name is empty (e.g. for IP certificates)
|
||||
$cert['descr'] = (string)$this->config->name . ' (ACME Client)';
|
||||
} else {
|
||||
$cert['descr'] = (string)$cert_cn . ' (ACME Client)';
|
||||
}
|
||||
$cert['descr'] = (string)$this->config->name . ' (ACME Client)';
|
||||
$import_log_message = 'imported';
|
||||
$cert_found = false;
|
||||
|
||||
|
|
@ -273,7 +267,7 @@ class LeCertificate extends LeCommon
|
|||
$newcert->crt = base64_encode($cert_content);
|
||||
$newcert->prv = base64_encode($key_content);
|
||||
}
|
||||
LeUtils::log("{$import_log_message} ACME X.509 certificate: {$cert_cn} ({$cert['refid']})");
|
||||
LeUtils::log("{$import_log_message} ACME X.509 certificate: {$this->config->name} ({$cert['refid']})");
|
||||
|
||||
// Serialize to config and save
|
||||
// Skip validation because the current in-memory model may not
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use OPNsense\Core\Config;
|
|||
* Google Domains DNS API
|
||||
* @package OPNsense\AcmeClient
|
||||
*/
|
||||
class DnsGoogleDomains extends Base implements LeValidationInterface
|
||||
class DnsGoogledomains extends Base implements LeValidationInterface
|
||||
{
|
||||
public function prepare()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<model>
|
||||
<mount>//OPNsense/AcmeClient</mount>
|
||||
<version>4.3.0</version>
|
||||
<version>4.3.1</version>
|
||||
<description>A secure ACME Client plugin</description>
|
||||
<items>
|
||||
<settings>
|
||||
|
|
@ -1430,7 +1430,8 @@
|
|||
<acme_ruckus>Upload certificate to Ruckus controller</acme_ruckus>
|
||||
<acme_vault>Upload certificate to HashiCorp Vault</acme_vault>
|
||||
<acme_synology_dsm>Upload certificate to Synology DSM</acme_synology_dsm>
|
||||
<acme_truenas>Upload certificate to TrueNAS Core Server</acme_truenas>
|
||||
<acme_truenas>Upload certificate to TrueNAS Server (deprecated API)</acme_truenas>
|
||||
<acme_truenasws>Upload certificate to TrueNAS Server (Websocket API)</acme_truenasws>
|
||||
<acme_zyxel_gs1900>Upload certificate to Zyxel GS1900 series switches</acme_zyxel_gs1900>
|
||||
<acme_unifi>Update local Unifi keystore</acme_unifi>
|
||||
<configd_generic>System or Plugin Command</configd_generic>
|
||||
|
|
@ -1744,6 +1745,25 @@
|
|||
<https>HTTPS</https>
|
||||
</OptionValues>
|
||||
</acme_truenas_scheme>
|
||||
<acme_truenasws_apikey type="TextField">
|
||||
<Required>N</Required>
|
||||
<Mask>/^.{1,1024}$/u</Mask>
|
||||
<ValidationMessage>Should be a string between 1 and 1024 characters.</ValidationMessage>
|
||||
</acme_truenasws_apikey>
|
||||
<acme_truenasws_hostname type="HostnameField">
|
||||
<Default>localhost</Default>
|
||||
<Required>N</Required>
|
||||
<Mask>/^.{1,1024}$/u</Mask>
|
||||
<ValidationMessage>Should be a string between 1 and 1024 characters.</ValidationMessage>
|
||||
</acme_truenasws_hostname>
|
||||
<acme_truenasws_protocol type="OptionField">
|
||||
<Default>ws</Default>
|
||||
<Required>N</Required>
|
||||
<OptionValues>
|
||||
<ws>ws [default]</ws>
|
||||
<wss>wss</wss>
|
||||
</OptionValues>
|
||||
</acme_truenasws_protocol>
|
||||
<acme_unifi_keystore type="TextField">
|
||||
<Default>/usr/local/share/java/unifi/data/keystore</Default>
|
||||
<Required>N</Required>
|
||||
|
|
|
|||
|
|
@ -99,224 +99,38 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* reload bootgrid, return to current selected page
|
||||
*/
|
||||
function std_bootgrid_reload(gridId) {
|
||||
var currentpage = $("#"+gridId).bootgrid("getCurrentPage");
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
// absolutely not perfect, bootgrid.reload doesn't seem to support when().done()
|
||||
setTimeout(function(){
|
||||
$('#'+gridId+'-footer a[data-page="'+currentpage+'"]').click();
|
||||
}, 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* copy actions for selected items from opnsense_bootgrid_plugin.js
|
||||
*/
|
||||
const grid_accounts = $("#grid-accounts").UIBootgrid($.extend(gridParams, { options: gridopt }));
|
||||
|
||||
$("#grid-accounts").on("loaded.rs.jquery.bootgrid", function (e)
|
||||
{
|
||||
// toggle all rendered tooltips (once for all)
|
||||
$('.bootgrid-tooltip').tooltip();
|
||||
|
||||
// scale footer on resize
|
||||
$(this).find("tfoot td:first-child").attr('colspan',$(this).find("th").length - 1);
|
||||
$(this).find('tr[data-row-id]').each(function(){
|
||||
if ($(this).find('[class*="command-toggle"]').first().data("value") == "0") {
|
||||
$(this).addClass("text-muted");
|
||||
const grid_accounts = $("#grid-accounts").UIBootgrid($.extend(gridParams, {
|
||||
options: gridopt,
|
||||
commands: {
|
||||
register: {
|
||||
method: function(event, cell) {
|
||||
var uuid = $(this).data("row-id");
|
||||
stdDialogConfirm(
|
||||
'{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Register the selected account with the configured ACME CA?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}',
|
||||
function() {
|
||||
ajaxCall(gridParams['register'] + uuid, {}, function() {
|
||||
grid_accounts.bootgrid("reload");
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
classname: 'fa fa-address-book-o',
|
||||
title: '{{ lang._('Register account') }}',
|
||||
sequence: 510
|
||||
}
|
||||
});
|
||||
|
||||
// edit dialog id to use
|
||||
var editDlg = $(this).attr('data-editDialog');
|
||||
var gridId = $(this).attr('id');
|
||||
|
||||
// link Add new to child button with data-action = add
|
||||
$(this).find("*[data-action=add]").click(function(){
|
||||
if ( gridParams['get'] != undefined && gridParams['add'] != undefined) {
|
||||
var urlMap = {};
|
||||
urlMap['frm_' + editDlg] = gridParams['get'];
|
||||
mapDataToFormUI(urlMap).done(function(){
|
||||
// update selectors
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
// clear validation errors (if any)
|
||||
clearFormValidation('frm_' + editDlg);
|
||||
});
|
||||
|
||||
// show dialog for edit
|
||||
$('#'+editDlg).modal({backdrop: 'static', keyboard: false});
|
||||
//
|
||||
$("#btn_"+editDlg+"_save").unbind('click').click(function(){
|
||||
saveFormToEndpoint(url=gridParams['add'],
|
||||
formid='frm_' + editDlg, callback_ok=function(){
|
||||
$("#"+editDlg).modal('hide');
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
}, true);
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action add missing")
|
||||
},
|
||||
tabulatorOptions: {
|
||||
rowFormatter: function(row) {
|
||||
if (parseInt(row.getData()['enabled'], 2) !== 1) {
|
||||
$(row.getElement()).addClass('text-muted');
|
||||
} else {
|
||||
$(row.getElement()).removeClass('text-muted');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// link delete selected items action
|
||||
$(this).find("*[data-action=deleteSelected]").click(function(){
|
||||
if ( gridParams['del'] != undefined) {
|
||||
stdDialogConfirm('{{ lang._('Confirm removal') }}',
|
||||
'{{ lang._('Do you want to remove the selected item?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function () {
|
||||
var rows =$("#"+gridId).bootgrid('getSelectedRows');
|
||||
if (rows != undefined){
|
||||
var deferreds = [];
|
||||
$.each(rows, function(key,uuid){
|
||||
deferreds.push(ajaxCall(url=gridParams['del'] + uuid, sendData={},null));
|
||||
});
|
||||
// refresh after load
|
||||
$.when.apply(null, deferreds).done(function(){
|
||||
std_bootgrid_reload(gridId);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action del missing")
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* copy actions for items from opnsense_bootgrid_plugin.js
|
||||
*/
|
||||
grid_accounts.on("loaded.rs.jquery.bootgrid", function(){
|
||||
|
||||
// edit dialog id to use
|
||||
var editDlg = $(this).attr('data-editDialog');
|
||||
var gridId = $(this).attr('id');
|
||||
|
||||
// edit item
|
||||
grid_accounts.find(".command-edit").on("click", function(e)
|
||||
{
|
||||
if (editDlg != undefined && gridParams['get'] != undefined) {
|
||||
var uuid = $(this).data("row-id");
|
||||
var urlMap = {};
|
||||
urlMap['frm_' + editDlg] = gridParams['get'] + uuid;
|
||||
mapDataToFormUI(urlMap).done(function () {
|
||||
// update selectors
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
// clear validation errors (if any)
|
||||
clearFormValidation('frm_' + editDlg);
|
||||
});
|
||||
|
||||
// show dialog for pipe edit
|
||||
$('#'+editDlg).modal({backdrop: 'static', keyboard: false});
|
||||
// define save action
|
||||
$("#btn_"+editDlg+"_save").unbind('click').click(function(){
|
||||
if (gridParams['set'] != undefined) {
|
||||
saveFormToEndpoint(url=gridParams['set']+uuid,
|
||||
formid='frm_' + editDlg, callback_ok=function(){
|
||||
$("#"+editDlg).modal('hide');
|
||||
std_bootgrid_reload(gridId);
|
||||
}, true);
|
||||
} else {
|
||||
console.log("[grid] action set missing")
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action get or data-editDialog missing")
|
||||
}
|
||||
});
|
||||
|
||||
// copy item, save as new
|
||||
grid_accounts.find(".command-copy").on("click", function(e)
|
||||
{
|
||||
if (editDlg != undefined && gridParams['get'] != undefined) {
|
||||
var uuid = $(this).data("row-id");
|
||||
var urlMap = {};
|
||||
urlMap['frm_' + editDlg] = gridParams['get'] + uuid;
|
||||
mapDataToFormUI(urlMap).done(function () {
|
||||
// update selectors
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
// clear validation errors (if any)
|
||||
clearFormValidation('frm_' + editDlg);
|
||||
});
|
||||
|
||||
// show dialog for pipe edit
|
||||
$('#'+editDlg).modal({backdrop: 'static', keyboard: false});
|
||||
// define save action
|
||||
$("#btn_"+editDlg+"_save").unbind('click').click(function(){
|
||||
if (gridParams['add'] != undefined) {
|
||||
saveFormToEndpoint(url=gridParams['add'],
|
||||
formid='frm_' + editDlg, callback_ok=function(){
|
||||
$("#"+editDlg).modal('hide');
|
||||
std_bootgrid_reload(gridId);
|
||||
}, true);
|
||||
} else {
|
||||
console.log("[grid] action add missing")
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action get or data-editDialog missing")
|
||||
}
|
||||
});
|
||||
|
||||
// delete item
|
||||
grid_accounts.find(".command-delete").on("click", function(e)
|
||||
{
|
||||
if (gridParams['del'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirm removal') }}',
|
||||
'{{ lang._('Do you want to remove the selected item?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function () {
|
||||
ajaxCall(url=gridParams['del'] + uuid,
|
||||
sendData={},callback=function(data,status){
|
||||
// reload grid after delete
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action del missing")
|
||||
}
|
||||
});
|
||||
|
||||
// toggle item
|
||||
grid_accounts.find(".command-toggle").on("click", function(e)
|
||||
{
|
||||
if (gridParams['toggle'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
$(this).addClass("fa-spinner fa-pulse");
|
||||
ajaxCall(url=gridParams['toggle'] + uuid,
|
||||
sendData={},callback=function(data,status){
|
||||
// reload grid after toggle
|
||||
std_bootgrid_reload(gridId);
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action toggle missing")
|
||||
}
|
||||
});
|
||||
|
||||
// register account
|
||||
grid_accounts.find(".command-register").on("click", function(e)
|
||||
{
|
||||
if (gridParams['register'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Register the selected account with the configured ACME CA?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
ajaxCall(url=gridParams['register'] + uuid,sendData={},callback=function(data,status){
|
||||
// reload grid afterwards
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action register missing")
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
// hook into on-show event for dialog to extend layout.
|
||||
$('#DialogAccount').on('shown.bs.modal', function (e) {
|
||||
|
|
|
|||
|
|
@ -124,302 +124,116 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* reload bootgrid, return to current selected page
|
||||
*/
|
||||
function std_bootgrid_reload(gridId) {
|
||||
var currentpage = $("#"+gridId).bootgrid("getCurrentPage");
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
// absolutely not perfect, bootgrid.reload doesn't seem to support when().done()
|
||||
setTimeout(function(){
|
||||
$('#'+gridId+'-footer a[data-page="'+currentpage+'"]').click();
|
||||
}, 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* copy actions for selected items from opnsense_bootgrid_plugin.js
|
||||
*/
|
||||
const grid_certificates = $("#grid-certificates").UIBootgrid($.extend(gridParams, { options: gridopt }));
|
||||
|
||||
$("#grid_certificates").on("loaded.rs.jquery.bootgrid", function (e)
|
||||
{
|
||||
// toggle all rendered tooltips (once for all)
|
||||
$('.bootgrid-tooltip').tooltip();
|
||||
|
||||
// scale footer on resize
|
||||
$(this).find("tfoot td:first-child").attr('colspan',$(this).find("th").length - 1);
|
||||
$(this).find('tr[data-row-id]').each(function(){
|
||||
if ($(this).find('[class*="command-toggle"]').first().data("value") == "0") {
|
||||
$(this).addClass("text-muted");
|
||||
const grid_certificates = $("#grid-certificates").UIBootgrid($.extend(gridParams, {
|
||||
options: gridopt,
|
||||
commands: {
|
||||
sign: {
|
||||
method: function(event, cell) {
|
||||
var uuid = $(this).data("row-id");
|
||||
stdDialogConfirm(
|
||||
'{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Forcefully issue or renew the selected certificate?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}',
|
||||
function() {
|
||||
// Handle HAProxy integration (no-op if not applicable)
|
||||
ajaxCall("/api/acmeclient/settings/fetch_ha_proxy_integration",
|
||||
{}, function(data, status) {
|
||||
ajaxCall(gridParams['sign'] + uuid, {}, function() {
|
||||
grid_certificates.bootgrid("reload");
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
classname: 'fa fa-repeat',
|
||||
title: '{{ lang._('Issue or renew certificate') }}',
|
||||
sequence: 510
|
||||
},
|
||||
import: {
|
||||
method: function(event, cell) {
|
||||
var uuid = $(this).data("row-id");
|
||||
stdDialogConfirm(
|
||||
'{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('(Re-) import the selected certificate and associated CA certificates into the trust storage?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}',
|
||||
function() {
|
||||
ajaxCall(gridParams['import'] + uuid, {}, function() {
|
||||
grid_certificates.bootgrid("reload");
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
classname: 'fa fa-certificate',
|
||||
title: '{{ lang._('(Re-) Import certificate') }}',
|
||||
sequence: 520
|
||||
},
|
||||
automation: {
|
||||
method: function(event, cell) {
|
||||
var uuid = $(this).data("row-id");
|
||||
stdDialogConfirm(
|
||||
'{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Rerun all automations for the selected certificate?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}',
|
||||
function() {
|
||||
ajaxCall(gridParams['automation'] + uuid, {}, function() {
|
||||
grid_certificates.bootgrid("reload");
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
classname: 'fa fa-paper-plane',
|
||||
title: '{{ lang._('Run automations') }}',
|
||||
sequence: 530
|
||||
},
|
||||
revoke: {
|
||||
method: function(event, cell) {
|
||||
var uuid = $(this).data("row-id");
|
||||
stdDialogConfirm(
|
||||
'{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Revoke selected certificate?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}',
|
||||
function() {
|
||||
ajaxCall(gridParams['revoke'] + uuid, {}, function() {
|
||||
grid_certificates.bootgrid("reload");
|
||||
});
|
||||
},
|
||||
'danger'
|
||||
);
|
||||
},
|
||||
classname: 'fa fa-power-off',
|
||||
title: '{{ lang._('Revoke certificate') }}',
|
||||
sequence: 540
|
||||
},
|
||||
removekey: {
|
||||
method: function(event, cell) {
|
||||
var uuid = $(this).data("row-id");
|
||||
stdDialogConfirm(
|
||||
'{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Really remove the private key?%s%sThe certificate will be completely reset. This is useful when the private key has been compromised or when you have changed the key options and want to regenerate the private key.%sNote that you have to revalidate the certificate afterwards in order to create a new private key and a matching certificate.') | format('<br/>', '<br/>', '<br/>') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}',
|
||||
function() {
|
||||
ajaxCall(gridParams['removekey'] + uuid, {}, function() {
|
||||
grid_certificates.bootgrid("reload");
|
||||
});
|
||||
},
|
||||
'danger'
|
||||
);
|
||||
},
|
||||
classname: 'fa fa-history',
|
||||
title: '{{ lang._('Reset certificate') }}',
|
||||
sequence: 550
|
||||
}
|
||||
});
|
||||
|
||||
// edit dialog id to use
|
||||
var editDlg = $(this).attr('data-editDialog');
|
||||
var gridId = $(this).attr('id');
|
||||
|
||||
// link Add new to child button with data-action = add
|
||||
$(this).find("*[data-action=add]").click(function(){
|
||||
if ( gridParams['get'] != undefined && gridParams['add'] != undefined) {
|
||||
var urlMap = {};
|
||||
urlMap['frm_' + editDlg] = gridParams['get'];
|
||||
mapDataToFormUI(urlMap).done(function(){
|
||||
// update selectors
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
// clear validation errors (if any)
|
||||
clearFormValidation('frm_' + editDlg);
|
||||
});
|
||||
|
||||
// show dialog for edit
|
||||
$('#'+editDlg).modal({backdrop: 'static', keyboard: false});
|
||||
//
|
||||
$("#btn_"+editDlg+"_save").unbind('click').click(function(){
|
||||
saveFormToEndpoint(url=gridParams['add'],
|
||||
formid='frm_' + editDlg, callback_ok=function(){
|
||||
$("#"+editDlg).modal('hide');
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
}, true);
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action add missing")
|
||||
},
|
||||
tabulatorOptions: {
|
||||
rowFormatter: function(row) {
|
||||
if (parseInt(row.getData()['enabled'], 2) !== 1) {
|
||||
$(row.getElement()).addClass('text-muted');
|
||||
} else {
|
||||
$(row.getElement()).removeClass('text-muted');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// link delete selected items action
|
||||
$(this).find("*[data-action=deleteSelected]").click(function(){
|
||||
if ( gridParams['del'] != undefined) {
|
||||
stdDialogConfirm('{{ lang._('Confirm removal') }}',
|
||||
'{{ lang._('Do you want to remove the selected item?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function () {
|
||||
var rows =$("#"+gridId).bootgrid('getSelectedRows');
|
||||
if (rows != undefined){
|
||||
var deferreds = [];
|
||||
$.each(rows, function(key,uuid){
|
||||
deferreds.push(ajaxCall(url=gridParams['del'] + uuid, sendData={},null));
|
||||
});
|
||||
// refresh after load
|
||||
$.when.apply(null, deferreds).done(function(){
|
||||
std_bootgrid_reload(gridId);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action del missing")
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* copy actions for items from opnsense_bootgrid_plugin.js
|
||||
*/
|
||||
grid_certificates.on("loaded.rs.jquery.bootgrid", function(){
|
||||
// edit dialog id to use
|
||||
var editDlg = $(this).attr('data-editDialog');
|
||||
var gridId = $(this).attr('id');
|
||||
|
||||
// edit item
|
||||
grid_certificates.find(".command-edit").on("click", function(e)
|
||||
{
|
||||
if (editDlg != undefined && gridParams['get'] != undefined) {
|
||||
var uuid = $(this).data("row-id");
|
||||
var urlMap = {};
|
||||
urlMap['frm_' + editDlg] = gridParams['get'] + uuid;
|
||||
mapDataToFormUI(urlMap).done(function () {
|
||||
// update selectors
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
// clear validation errors (if any)
|
||||
clearFormValidation('frm_' + editDlg);
|
||||
});
|
||||
|
||||
// show dialog for pipe edit
|
||||
$('#'+editDlg).modal({backdrop: 'static', keyboard: false});
|
||||
// define save action
|
||||
$("#btn_"+editDlg+"_save").unbind('click').click(function(){
|
||||
if (gridParams['set'] != undefined) {
|
||||
saveFormToEndpoint(url=gridParams['set']+uuid,
|
||||
formid='frm_' + editDlg, callback_ok=function(){
|
||||
$("#"+editDlg).modal('hide');
|
||||
std_bootgrid_reload(gridId);
|
||||
}, true);
|
||||
} else {
|
||||
console.log("[grid] action set missing")
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action get or data-editDialog missing")
|
||||
}
|
||||
});
|
||||
|
||||
// copy item, save as new
|
||||
grid_certificates.find(".command-copy").on("click", function(e)
|
||||
{
|
||||
if (editDlg != undefined && gridParams['get'] != undefined) {
|
||||
var uuid = $(this).data("row-id");
|
||||
var urlMap = {};
|
||||
urlMap['frm_' + editDlg] = gridParams['get'] + uuid;
|
||||
mapDataToFormUI(urlMap).done(function () {
|
||||
// update selectors
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
// clear validation errors (if any)
|
||||
clearFormValidation('frm_' + editDlg);
|
||||
});
|
||||
|
||||
// show dialog for pipe edit
|
||||
$('#'+editDlg).modal({backdrop: 'static', keyboard: false});
|
||||
// define save action
|
||||
$("#btn_"+editDlg+"_save").unbind('click').click(function(){
|
||||
if (gridParams['add'] != undefined) {
|
||||
saveFormToEndpoint(url=gridParams['add'],
|
||||
formid='frm_' + editDlg, callback_ok=function(){
|
||||
$("#"+editDlg).modal('hide');
|
||||
std_bootgrid_reload(gridId);
|
||||
}, true);
|
||||
} else {
|
||||
console.log("[grid] action add missing")
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action get or data-editDialog missing")
|
||||
}
|
||||
});
|
||||
|
||||
// delete item
|
||||
grid_certificates.find(".command-delete").on("click", function(e)
|
||||
{
|
||||
if (gridParams['del'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirm removal') }}',
|
||||
'{{ lang._('Do you want to remove the selected item?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function () {
|
||||
ajaxCall(url=gridParams['del'] + uuid,
|
||||
sendData={},callback=function(data,status){
|
||||
// reload grid after delete
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action del missing")
|
||||
}
|
||||
});
|
||||
|
||||
// toggle item
|
||||
grid_certificates.find(".command-toggle").on("click", function(e)
|
||||
{
|
||||
if (gridParams['toggle'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
$(this).addClass("fa-spinner fa-pulse");
|
||||
ajaxCall(url=gridParams['toggle'] + uuid,
|
||||
sendData={},callback=function(data,status){
|
||||
// reload grid after toggle
|
||||
std_bootgrid_reload(gridId);
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action toggle missing")
|
||||
}
|
||||
});
|
||||
|
||||
// sign cert
|
||||
grid_certificates.find(".command-sign").on("click", function(e)
|
||||
{
|
||||
if (gridParams['sign'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Forcefully issue or renew the selected certificate?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
// Handle HAProxy integration (no-op if not applicable)
|
||||
ajaxCall(url="/api/acmeclient/settings/fetch_ha_proxy_integration", sendData={}, callback=function(data,status) {
|
||||
ajaxCall(url=gridParams['sign'] + uuid,sendData={},callback=function(data,status){
|
||||
// reload grid after sign
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action sign missing")
|
||||
}
|
||||
});
|
||||
|
||||
// revoke cert
|
||||
grid_certificates.find(".command-revoke").on("click", function(e)
|
||||
{
|
||||
if (gridParams['revoke'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Revoke selected certificate?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
ajaxCall(url=gridParams['revoke'] + uuid,
|
||||
sendData={},callback=function(data,status){
|
||||
// reload grid after sign
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
});
|
||||
}, 'danger');
|
||||
} else {
|
||||
console.log("[grid] action revoke missing")
|
||||
}
|
||||
});
|
||||
|
||||
// remove private key
|
||||
grid_certificates.find(".command-removekey").on("click", function(e)
|
||||
{
|
||||
if (gridParams['removekey'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Really remove the private key?%s%sThe certificate will be completely reset. This is useful when the private key has been compromised or when you have changed the key options and want to regenerate the private key.%sNote that you have to revalidate the certificate afterwards in order to create a new private key and a matching certificate.') | format('<br/>', '<br/>', '<br/>') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
ajaxCall(url=gridParams['removekey'] + uuid,
|
||||
sendData={},callback=function(data,status){
|
||||
// reload grid after sign
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
});
|
||||
}, 'danger');
|
||||
} else {
|
||||
console.log("[grid] action removekey missing")
|
||||
}
|
||||
});
|
||||
|
||||
// run automation
|
||||
grid_certificates.find(".command-automation").on("click", function(e)
|
||||
{
|
||||
if (gridParams['automation'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('Rerun all automations for the selected certificate?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
ajaxCall(url=gridParams['automation'] + uuid,
|
||||
sendData={},callback=function(data,status){
|
||||
// reload grid after sign
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action automation missing")
|
||||
}
|
||||
});
|
||||
|
||||
// import certificate into trust storage
|
||||
grid_certificates.find(".command-import").on("click", function(e)
|
||||
{
|
||||
if (gridParams['import'] != undefined) {
|
||||
var uuid=$(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirmation Required') }}',
|
||||
'{{ lang._('(Re-) import the selected certificate and associated CA certificates into the trust storage?') }}',
|
||||
'{{ lang._('Yes') }}', '{{ lang._('Cancel') }}', function() {
|
||||
ajaxCall(url=gridParams['import'] + uuid,
|
||||
sendData={},callback=function(data,status){
|
||||
// reload grid after sign
|
||||
$("#"+gridId).bootgrid("reload");
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.log("[grid] action import missing")
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
// Hide options that are irrelevant in this context.
|
||||
$('#DialogCertificate').on('shown.bs.modal', function (e) {
|
||||
|
|
|
|||
|
|
@ -54,31 +54,6 @@
|
|||
search:'/api/diagnostics/log/core/acmeclient'
|
||||
});
|
||||
|
||||
grid_systemlog.on("loaded.rs.jquery.bootgrid", function(){
|
||||
$(".action-page").click(function(event){
|
||||
event.preventDefault();
|
||||
$("#grid-systemlog").bootgrid("search", "");
|
||||
let new_page = parseInt((parseInt($(this).data('row-id')) / $("#grid-log").bootgrid("getRowCount")))+1;
|
||||
$("input.search-field").val("");
|
||||
// XXX: a bit ugly, but clearing the filter triggers a load event.
|
||||
setTimeout(function(){
|
||||
$("ul.pagination > li:last > a").data('page', new_page).click();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
grid_acmelog.on("loaded.rs.jquery.bootgrid", function(){
|
||||
$(".action-page").click(function(event){
|
||||
event.preventDefault();
|
||||
$("#grid-acmelog").bootgrid("search", "");
|
||||
let new_page = parseInt((parseInt($(this).data('row-id')) / $("#grid-log").bootgrid("getRowCount")))+1;
|
||||
$("input.search-field").val("");
|
||||
// XXX: a bit ugly, but clearing the filter triggers a load event.
|
||||
setTimeout(function(){
|
||||
$("ul.pagination > li:last > a").data('page', new_page).click();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Plugin Changelog
|
|||
|
||||
1.8.1
|
||||
|
||||
* Fixed detect broken executables option (contributed by sopex)
|
||||
* Fixed detect broken executables option (contributed by Konstantinos Spartalis)
|
||||
|
||||
1.8
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_NAME= netbird
|
||||
PLUGIN_VERSION= 1.1
|
||||
PLUGIN_VERSION= 1.2
|
||||
PLUGIN_DEPENDS= netbird
|
||||
PLUGIN_COMMENT= Peer-to-peer VPN that seamlessly connects your devices
|
||||
PLUGIN_MAINTAINER= dev@netbird.io
|
||||
|
|
|
|||
|
|
@ -42,6 +42,36 @@
|
|||
<type>checkbox</type>
|
||||
<help>Allows incoming SSH connections</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.ssh.enableRoot</id>
|
||||
<label>Enable Root Login</label>
|
||||
<type>checkbox</type>
|
||||
<help>Allow root user login</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.ssh.enableSFTP</id>
|
||||
<label>Enable SFTP</label>
|
||||
<type>checkbox</type>
|
||||
<help>Enable SFTP subsystem for file transfers</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.ssh.enableLocalPortForwarding</id>
|
||||
<label>Enable Local Port Forwarding</label>
|
||||
<type>checkbox</type>
|
||||
<help>Allow clients to forward local ports through the server</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.ssh.enableRemotePortForwarding</id>
|
||||
<label>Enable Remote Port Forwarding</label>
|
||||
<type>checkbox</type>
|
||||
<help>Allow clients to request remote port forwarding</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>settings.ssh.enableAuth</id>
|
||||
<label>Enable SSH Authentication</label>
|
||||
<type>checkbox</type>
|
||||
<help>Enable JWT authentication for SSH connections. When disabled, allows any peer with network access</help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>DNS</label>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ class Settings extends BaseModel
|
|||
|
||||
$config["WgPort"] = (int)$this->general->wireguardPort->__toString();
|
||||
$config["ServerSSHAllowed"] = $this->ssh->enable->__toString() == 1;
|
||||
$config["EnableSSHRoot"] = $this->ssh->enableRoot->__toString() == 1;
|
||||
$config["EnableSSHSFTP"] = $this->ssh->enableSFTP->__toString() == 1;
|
||||
$config["EnableSSHLocalPortForwarding"] = $this->ssh->enableLocalPortForwarding->__toString() == 1;
|
||||
$config["EnableSSHRemotePortForwarding"] = $this->ssh->enableRemotePortForwarding->__toString() == 1;
|
||||
$config["DisableSSHAuth"] = $this->ssh->enableAuth->__toString() != 1;
|
||||
$config["DisableFirewall"] = $this->firewall->allowConfig->__toString() != 1;
|
||||
$config["BlockInbound"] = $this->firewall->blockInboundConnection->__toString() == 1;
|
||||
$config["DisableDNS"] = $this->dns->enable->__toString() != 1;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<model>
|
||||
<mount>//OPNsense/netbird/settings</mount>
|
||||
<description>NetBird settings</description>
|
||||
<version>1.1.0</version>
|
||||
<version>1.2.0</version>
|
||||
<items>
|
||||
<general>
|
||||
<enable type="BooleanField">
|
||||
|
|
@ -31,6 +31,26 @@
|
|||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</enable>
|
||||
<enableRoot type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</enableRoot>
|
||||
<enableSFTP type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</enableSFTP>
|
||||
<enableLocalPortForwarding type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</enableLocalPortForwarding>
|
||||
<enableRemotePortForwarding type="BooleanField">
|
||||
<Default>0</Default>
|
||||
<Required>Y</Required>
|
||||
</enableRemotePortForwarding>
|
||||
<enableAuth type="BooleanField">
|
||||
<Default>1</Default>
|
||||
<Required>Y</Required>
|
||||
</enableAuth>
|
||||
</ssh>
|
||||
<dns>
|
||||
<enable type="BooleanField">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
PLUGIN_NAME= openvpn-legacy
|
||||
PLUGIN_VERSION= 1.0
|
||||
PLUGIN_REVISION= 1
|
||||
PLUGIN_COMMENT= OpenVPN legacy support
|
||||
PLUGIN_DEPENDS= # openvpn
|
||||
PLUGIN_MAINTAINER= ad@opnsense.org
|
||||
|
|
|
|||
|
|
@ -1474,7 +1474,7 @@ $( document ).ready(function() {
|
|||
</span><br>
|
||||
<select name='netbios_ntype' class="selectpicker">
|
||||
<?php
|
||||
foreach ($netbios_nodetypes as $type => $name) :
|
||||
foreach (['0' => 'none', '1' => 'b-node', '2' => 'p-node', '4' => 'm-node', '5' => 'h-node'] as $type => $name):
|
||||
$selected = "";
|
||||
if ($pconfig['netbios_ntype'] == $type) {
|
||||
$selected = "selected=\"selected\"";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
PLUGIN_NAME= q-feeds-connector
|
||||
PLUGIN_VERSION= 1.4
|
||||
PLUGIN_VERSION= 1.5
|
||||
PLUGIN_REVISION= 1
|
||||
PLUGIN_COMMENT= Connector for Q-Feeds threat intel
|
||||
PLUGIN_MAINTAINER= devel@qfeeds.com
|
||||
PLUGIN_TIER= 2
|
||||
|
|
|
|||
|
|
@ -3,9 +3,18 @@ Connector for Q-Feeds threat intel
|
|||
Plugin Changelog
|
||||
================
|
||||
|
||||
1.5
|
||||
|
||||
* Feature: Add passlist option for unbound
|
||||
* Feature: Add effective networks for unbound
|
||||
* Feature: Add NXDOMAIN option for unbound
|
||||
* Feature: Add dest address for unbound
|
||||
* Bugfix: Invalidate alias cache on reconfigure
|
||||
|
||||
1.4
|
||||
|
||||
* Feature: Added DNSCrypt-Proxy integration
|
||||
* Bugfix: Track loaded lists when deselected and reload Unbounds blocklist in that case
|
||||
|
||||
1.3
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ use OPNsense\Base\ApiMutableModelControllerBase;
|
|||
use OPNsense\Base\UserException;
|
||||
use OPNsense\Core\Backend;
|
||||
use OPNsense\Core\Config;
|
||||
use OPNsense\Firewall\Alias;
|
||||
|
||||
class SettingsController extends ApiMutableModelControllerBase
|
||||
{
|
||||
|
|
@ -51,6 +52,8 @@ class SettingsController extends ApiMutableModelControllerBase
|
|||
if (strpos($res, 'EXIT OK') === false) {
|
||||
throw new UserException($res);
|
||||
}
|
||||
/* as we register new dynamic aliases, we're also responsible for invalidating an existing cache */
|
||||
Alias::flushCacheData();
|
||||
return ['status' => 'ok', 'output' => $res];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,44 @@
|
|||
<id>connect.general.enable_unbound_bl</id>
|
||||
<label>Register domain feeds</label>
|
||||
<type>checkbox</type>
|
||||
<help>Use domain feeds in Unbound DNS blocklist, requires blocklists to be enabled in order to have effect</help>
|
||||
<help>Use domain feeds in Unbound DNS and DNScrypt-proxy blocklists, requires blocklists to be enabled in order to have effect</help>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Unbound blocklist settings</label>
|
||||
<style>unbound_options</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>connect.unbound.allowlists</id>
|
||||
<label>Allowlist Domains</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<allownew>true</allownew>
|
||||
<help>List of domains to allow. You can use regular expressions. This allow list only applies to blocklist matches on items in this policy.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>connect.unbound.source_nets</id>
|
||||
<label>Source Net(s)</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<allownew>true</allownew>
|
||||
<help>Source networks to apply policy on. Examples are 192.168.1.0/24 or 192.168.1.1. Leave empty to apply on everything. All specified networks should use the same protocol family and have equal sizes to avoid priority issues. </help>
|
||||
</field>
|
||||
<field>
|
||||
<id>connect.unbound.address</id>
|
||||
<label>Destination Address</label>
|
||||
<type>text</type>
|
||||
<advanced>true</advanced>
|
||||
<help>
|
||||
Destination ip address for entries in the blocklist (leave empty to use default: 0.0.0.0).
|
||||
Not used when "Return NXDOMAIN" is checked.
|
||||
</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>connect.unbound.nxdomain</id>
|
||||
<label>Return NXDOMAIN</label>
|
||||
<type>checkbox</type>
|
||||
<advanced>true</advanced>
|
||||
<help>Use the DNS response code NXDOMAIN instead of a destination address.</help>
|
||||
</field>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -7,5 +7,21 @@
|
|||
<apikey type="TextField"/>
|
||||
<enable_unbound_bl type="BooleanField"/>
|
||||
</general>
|
||||
<unbound>
|
||||
<allowlists type="CSVListField"/>
|
||||
<source_nets type="NetworkField">
|
||||
<Multiple>Y</Multiple>
|
||||
<Strict>Y</Strict>
|
||||
<ValidationMessage>Please specify a valid network segment or address (IPv4/IPv6). If a mask is provided, please omit the host bits.</ValidationMessage>
|
||||
<WildcardEnabled>N</WildcardEnabled>
|
||||
<NetMaskRequired>N</NetMaskRequired>
|
||||
<AsList>Y</AsList>
|
||||
</source_nets>
|
||||
<address type="NetworkField">
|
||||
<NetMaskAllowed>N</NetMaskAllowed>
|
||||
<AddressFamily>ipv4</AddressFamily>
|
||||
</address>
|
||||
<nxdomain type="BooleanField"/>
|
||||
</unbound>
|
||||
</items>
|
||||
</model>
|
||||
|
|
|
|||
|
|
@ -99,6 +99,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
}
|
||||
});
|
||||
|
||||
$("#connect\\.general\\.enable_unbound_bl").change(function(){
|
||||
if ($(this).is(':checked')) {
|
||||
$(".unbound_options").closest('table').show();
|
||||
} else {
|
||||
$(".unbound_options").closest('table').hide();
|
||||
}
|
||||
});
|
||||
|
||||
let selected_tab = window.location.hash != "" ? window.location.hash : "#settings";
|
||||
$('a[href="' +selected_tab + '"]').tab('show');
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class QFeedsConfig:
|
|||
cnf = ConfigParser()
|
||||
cnf.read(config_filename)
|
||||
if cnf.has_section('api') and cnf.has_option('api', 'key'):
|
||||
self.api_key = cnf.get('api', 'key')
|
||||
self.api_key = cnf.get('api', 'key').strip()
|
||||
|
||||
|
||||
class Api:
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ class PFLogCrawler:
|
|||
# quick scan for datetime, interface, direction, source, dest, source_port, dest_port
|
||||
parts = line.split()
|
||||
fw_line = parts[-1].split(',') # strip syslog
|
||||
if fw_line[6] == 'pass':
|
||||
return []
|
||||
ip_addresses = [x for x in fw_line if is_ip_address(x)]
|
||||
# Find destination IP position to get ports from next fields (only if numeric)
|
||||
dest_idx = fw_line.index(ip_addresses[1]) if len(ip_addresses) > 1 else len(fw_line)
|
||||
|
|
@ -77,8 +79,10 @@ class PFLogCrawler:
|
|||
for idx, line in enumerate(f_in):
|
||||
for rule_id in self._rule_ids:
|
||||
if rule_id in line:
|
||||
result.append(self._parse_log_line(line))
|
||||
rows_processed +=1
|
||||
lline = self._parse_log_line(line)
|
||||
if lline:
|
||||
result.append(lline)
|
||||
rows_processed +=1
|
||||
break # inner loop
|
||||
if (idx % 100000 == 0 and time.time() - start_time > max_time) or rows_processed >= max_results:
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/local/bin/python3
|
||||
|
||||
"""
|
||||
Copyright (c) 2025-2026 Deciso B.V.
|
||||
Copyright (c) 2025 Deciso B.V.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/local/bin/python3
|
||||
|
||||
"""
|
||||
Copyright (c) 2025 Deciso B.V.
|
||||
Copyright (c) 2025-2026 Deciso B.V.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -27,12 +27,19 @@
|
|||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import syslog
|
||||
import uuid
|
||||
from . import BaseBlocklistHandler
|
||||
|
||||
class DefaultBlocklistHandler(BaseBlocklistHandler):
|
||||
class QFeedsBlocklistHandler(BaseBlocklistHandler):
|
||||
def __init__(self):
|
||||
super().__init__('/usr/local/etc/unbound/qfeeds-blocklists.conf')
|
||||
self.priority = 50
|
||||
self._compat_id = str(uuid.uuid4())
|
||||
|
||||
def _is_enabled(self):
|
||||
return self.cnf and self.cnf.has_section('settings') and self.cnf.has_option('settings', 'filenames')
|
||||
|
||||
def get_config(self):
|
||||
# do not use, unbound worker settings
|
||||
|
|
@ -41,11 +48,10 @@ class DefaultBlocklistHandler(BaseBlocklistHandler):
|
|||
def get_blocklist(self):
|
||||
# Only return domains if integration is enabled (filenames are offered)
|
||||
qfeeds_filenames = []
|
||||
if self.cnf and self.cnf.has_section('settings'):
|
||||
if self.cnf.has_option('settings', 'filenames'):
|
||||
qfeeds_filenames = self.cnf.get('settings', 'filenames').split(',')
|
||||
# touch a file to help qfeedsctl detect the current instance uses its list
|
||||
open('/tmp/qfeeds-unbound-bl.stat', 'w').write('')
|
||||
if self._is_enabled():
|
||||
qfeeds_filenames = self.cnf.get('settings', 'filenames').split(',')
|
||||
# touch a file to help qfeedsctl detect the current instance uses its list
|
||||
open('/tmp/qfeeds-unbound-bl.stat', 'w').write('')
|
||||
|
||||
result = {}
|
||||
for filename in qfeeds_filenames:
|
||||
|
|
@ -58,3 +64,35 @@ class DefaultBlocklistHandler(BaseBlocklistHandler):
|
|||
|
||||
def get_passlist_patterns(self):
|
||||
return []
|
||||
|
||||
def get_policies(self):
|
||||
if not self._is_enabled():
|
||||
return []
|
||||
|
||||
cfg = {
|
||||
'source_nets': [],
|
||||
'address': '',
|
||||
'rcode': '',
|
||||
'id': self._compat_id,
|
||||
'allowlists': []
|
||||
}
|
||||
for k,v in self.cnf['settings'].items():
|
||||
if k in cfg and v.strip() != '':
|
||||
if type(cfg[k]) is list:
|
||||
cfg[k] = v.split(',')
|
||||
else:
|
||||
cfg[k] = v.strip()
|
||||
|
||||
if cfg['allowlists']:
|
||||
compiled_passlist = set()
|
||||
for pattern in cfg['allowlists']:
|
||||
try:
|
||||
re.compile(pattern, re.IGNORECASE)
|
||||
compiled_passlist.add(pattern)
|
||||
except re.error:
|
||||
syslog.syslog(syslog.LOG_ERR,'Q-Feeds : skip invalid whitelist exclude pattern "%s"' % pattern)
|
||||
|
||||
cfg['passlist'] = '|'.join(compiled_passlist)
|
||||
del cfg['allowlists']
|
||||
|
||||
return [cfg]
|
||||
|
|
|
|||
|
|
@ -2,4 +2,11 @@
|
|||
not helpers.empty('OPNsense.QFeedsConnector.general.enable_unbound_bl') %}
|
||||
[settings]
|
||||
filenames=/var/db/qfeeds-tables/malware_domains.txt
|
||||
{% if not helpers.empty('OPNsense.QFeedsConnector.unbound') %}
|
||||
allowlists={{OPNsense.QFeedsConnector.unbound.allowlists|default('')}}
|
||||
source_nets={{OPNsense.QFeedsConnector.unbound.source_nets|default('')}}
|
||||
address={{OPNsense.QFeedsConnector.unbound.address|default('0.0.0.0')}}
|
||||
rcode={% if OPNsense.QFeedsConnector.unbound.nxdomain|default('0') == '1' %}NXDOMAIN{%else%}NOERROR{%endif +%}
|
||||
cache_ttl={{OPNsense.QFeedsConnector.unbound.cache_ttl|default('72000')}}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_NAME= tailscale
|
||||
PLUGIN_VERSION= 1.3
|
||||
PLUGIN_VERSION= 1.4
|
||||
PLUGIN_COMMENT= VPN mesh securely connecting clients using WireGuard
|
||||
PLUGIN_DEPENDS= tailscale
|
||||
PLUGIN_MAINTAINER= sam@sheridan.uk
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue