diff --git a/plist b/plist index 16cb043288f..9ee5619aba2 100644 --- a/plist +++ b/plist @@ -432,6 +432,10 @@ /usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml /usr/local/opnsense/mvc/app/controllers/OPNsense/Ntpd/Api/ServiceController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/Ntpd/StatusController.php +/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenDNS/Api/ServiceController.php +/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenDNS/Api/SettingsController.php +/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenDNS/SettingsController.php +/usr/local/opnsense/mvc/app/controllers/OPNsense/OpenDNS/forms/general.xml /usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/Api/ClientOverwritesController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/Api/ExportController.php /usr/local/opnsense/mvc/app/controllers/OPNsense/OpenVPN/Api/InstancesController.php @@ -866,6 +870,10 @@ /usr/local/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml /usr/local/opnsense/mvc/app/models/OPNsense/Ntpd/ACL/ACL.xml /usr/local/opnsense/mvc/app/models/OPNsense/Ntpd/Menu/Menu.xml +/usr/local/opnsense/mvc/app/models/OPNsense/OpenDNS/ACL/ACL.xml +/usr/local/opnsense/mvc/app/models/OPNsense/OpenDNS/Menu/Menu.xml +/usr/local/opnsense/mvc/app/models/OPNsense/OpenDNS/OpenDNS.php +/usr/local/opnsense/mvc/app/models/OPNsense/OpenDNS/OpenDNS.xml /usr/local/opnsense/mvc/app/models/OPNsense/OpenVPN/Export.php /usr/local/opnsense/mvc/app/models/OPNsense/OpenVPN/Export.xml /usr/local/opnsense/mvc/app/models/OPNsense/OpenVPN/FieldTypes/InstanceField.php @@ -1016,6 +1024,7 @@ /usr/local/opnsense/mvc/app/views/OPNsense/Monit/index.volt /usr/local/opnsense/mvc/app/views/OPNsense/Monit/status.volt /usr/local/opnsense/mvc/app/views/OPNsense/Ntpd/status.volt +/usr/local/opnsense/mvc/app/views/OPNsense/OpenDNS/settings.volt /usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/cso.volt /usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/export.volt /usr/local/opnsense/mvc/app/views/OPNsense/OpenVPN/instances.volt @@ -1327,6 +1336,7 @@ /usr/local/opnsense/scripts/netflow/lib/parse.py /usr/local/opnsense/scripts/ntpd/ntpd_status.php /usr/local/opnsense/scripts/openssh/ssh_query.py +/usr/local/opnsense/scripts/opendns/configure.php /usr/local/opnsense/scripts/openvpn/client_connect.php /usr/local/opnsense/scripts/openvpn/client_disconnect.sh /usr/local/opnsense/scripts/openvpn/genkey.py @@ -1455,6 +1465,7 @@ /usr/local/opnsense/service/conf/actions.d/actions_monit.conf /usr/local/opnsense/service/conf/actions.d/actions_netflow.conf /usr/local/opnsense/service/conf/actions.d/actions_ntpd.conf +/usr/local/opnsense/service/conf/actions.d/actions_opendns.conf /usr/local/opnsense/service/conf/actions.d/actions_openssh.conf /usr/local/opnsense/service/conf/actions.d/actions_openvpn.conf /usr/local/opnsense/service/conf/actions.d/actions_radvd.conf @@ -2550,7 +2561,6 @@ /usr/local/www/services_ntpd.php /usr/local/www/services_ntpd_gps.php /usr/local/www/services_ntpd_pps.php -/usr/local/www/services_opendns.php /usr/local/www/status_wireless.php /usr/local/www/system_advanced_admin.php /usr/local/www/system_advanced_firewall.php diff --git a/src/etc/inc/plugins.inc.d/opendns.inc b/src/etc/inc/plugins.inc.d/opendns.inc index a456a17a118..8fd604cf71d 100644 --- a/src/etc/inc/plugins.inc.d/opendns.inc +++ b/src/etc/inc/plugins.inc.d/opendns.inc @@ -1,6 +1,7 @@ enable->isEmpty()) { service_log('Configure OpenDNS...', $verbose); - $result = opendns_register($config['opendns']); + $pconfig = [ + 'username' => (string)$mdl->username, + 'password' => (string)$mdl->password, + 'host' => (string)$mdl->host, + ]; + $result = opendns_register($pconfig); log_msg("opendns response: $result"); service_log("done.\n", $verbose); @@ -53,7 +59,7 @@ function opendns_xmlrpc_sync() { return [[ 'description' => gettext('OpenDNS'), - 'section' => 'opendns', + 'section' => 'OPNsense.OpenDNS', 'id' => 'opendns', ]]; } @@ -64,6 +70,7 @@ function opendns_register($pconfig) curl_setopt($ch, CURLOPT_URL, sprintf('https://updates.opendns.com/nic/update?hostname=%s', $pconfig['host'])); curl_setopt($ch, CURLOPT_USERPWD, sprintf('%s:%s', $pconfig['username'], $pconfig['password'])); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 60); $output = curl_exec($ch); curl_close($ch); diff --git a/src/opnsense/mvc/app/controllers/OPNsense/OpenDNS/Api/ServiceController.php b/src/opnsense/mvc/app/controllers/OPNsense/OpenDNS/Api/ServiceController.php new file mode 100644 index 00000000000..eec74ae555c --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/OpenDNS/Api/ServiceController.php @@ -0,0 +1,48 @@ + 'failed']; + if ($this->request->isPost()) { + $result['status'] = trim((new Backend())->configdRun('opendns configure')); + } + return $result; + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/OpenDNS/Api/SettingsController.php b/src/opnsense/mvc/app/controllers/OPNsense/OpenDNS/Api/SettingsController.php new file mode 100644 index 00000000000..e47d317bb9c --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/OpenDNS/Api/SettingsController.php @@ -0,0 +1,37 @@ +view->generalForm = $this->getForm('general'); + $this->view->pick('OPNsense/OpenDNS/settings'); + } +} diff --git a/src/opnsense/mvc/app/controllers/OPNsense/OpenDNS/forms/general.xml b/src/opnsense/mvc/app/controllers/OPNsense/OpenDNS/forms/general.xml new file mode 100644 index 00000000000..26ba7dc52ac --- /dev/null +++ b/src/opnsense/mvc/app/controllers/OPNsense/OpenDNS/forms/general.xml @@ -0,0 +1,32 @@ +
+ + opendns.enable + + checkbox + OpenDNS.com, unless Standalone mode is enabled. The DNS servers configured under System: Settings: General will be overwritten, and any DNS servers learned by DHCP/PPP on WAN will be ignored. The previous DNS settings will be saved, and later restored when OpenDNS is disabled.]]> + + + opendns.standalone + + checkbox + If this option is enabled, the system's DNS server settings will not be altered to use the DNS servers from OpenDNS.com. This mode is useful when the OpenDNS servers are used by a different network component but the periodic update behavior is still desired. + + + opendns.username + + text + Login username for the OpenDNS.com dashboard. Used to automatically update the IP address of the registered network. + + + opendns.password + + password + Login password for the OpenDNS.com dashboard. + + + opendns.host + + text + settings dashboard of OpenDNS.com. Used to update the node's IP address whenever the WAN interface changes its IP address.]]> + +
diff --git a/src/opnsense/mvc/app/models/OPNsense/OpenDNS/ACL/ACL.xml b/src/opnsense/mvc/app/models/OPNsense/OpenDNS/ACL/ACL.xml new file mode 100644 index 00000000000..e8b74df3d49 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/OpenDNS/ACL/ACL.xml @@ -0,0 +1,10 @@ + + + Services: OpenDNS + + ui/opendns/settings + api/opendns/settings/* + api/opendns/service/* + + + diff --git a/src/opnsense/mvc/app/models/OPNsense/OpenDNS/Menu/Menu.xml b/src/opnsense/mvc/app/models/OPNsense/OpenDNS/Menu/Menu.xml new file mode 100644 index 00000000000..5a9cb8f227f --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/OpenDNS/Menu/Menu.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/opnsense/mvc/app/models/OPNsense/OpenDNS/OpenDNS.php b/src/opnsense/mvc/app/models/OPNsense/OpenDNS/OpenDNS.php new file mode 100644 index 00000000000..06eb7615046 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/OpenDNS/OpenDNS.php @@ -0,0 +1,58 @@ +enable->isEmpty()) { + return $messages; + } + foreach (['username', 'password', 'host'] as $fieldname) { + $node = $this->$fieldname; + if ($validateFullModel || $node->isFieldChanged()) { + if (trim((string)$node) === '') { + $messages->appendMessage(new Message( + gettext('A value is required when OpenDNS is enabled.'), + $fieldname + )); + } + } + } + return $messages; + } +} diff --git a/src/opnsense/mvc/app/models/OPNsense/OpenDNS/OpenDNS.xml b/src/opnsense/mvc/app/models/OPNsense/OpenDNS/OpenDNS.xml new file mode 100644 index 00000000000..2d210ea40e9 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/OpenDNS/OpenDNS.xml @@ -0,0 +1,28 @@ + + //opendns + 1.0.0 + OpenDNS configuration + + + 0 + + + 0 + + + + + /^[a-zA-Z0-9 _\-\.]+$/ + Please specify a valid OpenDNS network label. + + + + 0 + + + + 1 + + + + diff --git a/src/opnsense/mvc/app/views/OPNsense/OpenDNS/settings.volt b/src/opnsense/mvc/app/views/OPNsense/OpenDNS/settings.volt new file mode 100644 index 00000000000..4a1f4c91558 --- /dev/null +++ b/src/opnsense/mvc/app/views/OPNsense/OpenDNS/settings.volt @@ -0,0 +1,48 @@ +{# + # Copyright (C) 2026 Greelan + # 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. + #} + + + +
+ {{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_settings'])}} +
+{{ partial('layout_partials/base_apply_button', {'data_endpoint': '/api/opendns/service/reconfigure', 'data_error_title': lang._('Error applying OpenDNS configuration')}) }} diff --git a/src/opnsense/scripts/opendns/configure.php b/src/opnsense/scripts/opendns/configure.php new file mode 100644 index 00000000000..81da7b16e4b --- /dev/null +++ b/src/opnsense/scripts/opendns/configure.php @@ -0,0 +1,115 @@ +#!/usr/local/bin/php + + * Copyright (c) 2008 Tellnet AG + * 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. + */ + +require_once('config.inc'); +require_once('util.inc'); +require_once('plugins.inc.d/opendns.inc'); + +use OPNsense\OpenDNS\OpenDNS; + +$mdl = new OpenDNS(); +$enabled = !$mdl->enable->isEmpty(); +$standalone = !$mdl->standalone->isEmpty(); +$has_backup = (string)$mdl->backup->has_backup == '1'; + +if ($enabled) { + $result = trim(opendns_register([ + 'username' => (string)$mdl->username, + 'password' => (string)$mdl->password, + 'host' => (string)$mdl->host, + ])); + $errors = []; + foreach (explode("\n", $result) as $line) { + $line = trim($line); + if ($line === '' || strpos($line, 'good') === 0 || $line === 'noop') { + continue; + } + $errors[] = $line; + } + if (!empty($errors)) { + echo "OpenDNS.com registration failed: " . implode("\n", $errors); + exit(1); + } +} + +$system = &config_read_array('system'); + +if ($enabled && $standalone) { + /* standalone mode: do not alter DNS server settings */ +} elseif ($enabled) { + /* capture current DNS settings before overwriting, + * but only if we don't already have a backup + * (avoids re-capturing OpenDNS servers on subsequent applies) */ + if (!$has_backup) { + $mdl->backup->has_backup = '1'; + $mdl->backup->dnsservers = implode(',', $system['dnsserver'] ?? []); + $mdl->backup->dnsallowoverride = $system['dnsallowoverride'] ?? '1'; + $mdl->serializeToConfig(false, true); + } + + $system['dnsserver'] = []; + $v4_server = ['208.67.222.222', '208.67.220.220']; + $v6_server = ['2620:119:35::35', '2620:119:53::53']; + if (isset($system['prefer_ipv4'])) { + $system['dnsserver'][] = $v4_server[0]; + $system['dnsserver'][] = $v4_server[1]; + if (is_ipv6_allowed()) { + $system['dnsserver'][] = $v6_server[0]; + $system['dnsserver'][] = $v6_server[1]; + } + } else { + if (is_ipv6_allowed()) { + $system['dnsserver'][] = $v6_server[0]; + $system['dnsserver'][] = $v6_server[1]; + } + $system['dnsserver'][] = $v4_server[0]; + $system['dnsserver'][] = $v4_server[1]; + } + $system['dnsallowoverride'] = '0'; +} else { + /* disabled: restore backup if available, otherwise fall back to defaults */ + if ($has_backup) { + $servers = explode(',', (string)$mdl->backup->dnsservers); + $system['dnsserver'] = !empty(array_filter($servers)) ? $servers : ['']; + $system['dnsallowoverride'] = (string)$mdl->backup->dnsallowoverride; + + /* clear the backup */ + $mdl->backup->has_backup = '0'; + $mdl->backup->dnsservers = ''; + $mdl->backup->dnsallowoverride = '1'; + $mdl->serializeToConfig(false, true); + } else { + $system['dnsserver'] = ['']; + $system['dnsallowoverride'] = '1'; + } +} + +write_config('OpenDNS filter configuration change'); diff --git a/src/opnsense/service/conf/actions.d/actions_opendns.conf b/src/opnsense/service/conf/actions.d/actions_opendns.conf new file mode 100644 index 00000000000..1a609b37b06 --- /dev/null +++ b/src/opnsense/service/conf/actions.d/actions_opendns.conf @@ -0,0 +1,4 @@ +[configure] +command:/usr/local/opnsense/scripts/opendns/configure.php && { /usr/local/sbin/pluginctl -c dns_reload > /dev/null || { echo "dns_reload failed"; exit 1; }; } && { /usr/local/sbin/pluginctl -c dhcp > /dev/null || { echo "dhcp reload failed"; exit 1; }; } && echo ok +type:script_output +message:Configuring OpenDNS diff --git a/src/www/services_opendns.php b/src/www/services_opendns.php deleted file mode 100644 index 5632080c5d5..00000000000 --- a/src/www/services_opendns.php +++ /dev/null @@ -1,237 +0,0 @@ - - * Copyright (c) 2008 Tellnet AG - * 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. - */ - -require_once("guiconfig.inc"); -require_once("system.inc"); -require_once("interfaces.inc"); -require_once("plugins.inc.d/opendns.inc"); - -config_read_array('opendns'); - -if ($_SERVER['REQUEST_METHOD'] === 'GET') { - $pconfig['enable'] = isset($config['opendns']['enable']); - $pconfig['standalone'] = isset($config['opendns']['standalone']); - $pconfig['username'] = !empty($config['opendns']['username']) ? $config['opendns']['username'] : null; - $pconfig['password'] = !empty($config['opendns']['password']) ? $config['opendns']['password'] : null; - $pconfig['host'] = !empty($config['opendns']['host']) ? $config['opendns']['host'] : null; -} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { - $input_errors = array(); - $pconfig = $_POST; - - /* input validation */ - $reqdfields = array(); - $reqdfieldsn = array(); - if (!empty($pconfig['enable'])) { - $reqdfields = array_merge($reqdfields, explode(" ", "host username password")); - $reqdfieldsn = array_merge($reqdfieldsn, explode(",", "Network,Username,Password")); - } - do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); - - if (!empty($pconfig['host']) && !is_domain($pconfig['host'])) { - $input_errors[] = 'The host name contains invalid characters.'; - } - if (empty($pconfig['username'])) { - $input_errors[] = 'The username cannot be empty.'; - } - - if (!empty($pconfig['test'])) { - $test_results = explode("\r\n", opendns_register($pconfig)); - } elseif (count($input_errors) == 0) { - $config['opendns']['enable'] = !empty($pconfig['enable']); - $config['opendns']['standalone'] = !empty($pconfig['standalone']); - $config['opendns']['username'] = $pconfig['username']; - $config['opendns']['password'] = $pconfig['password']; - $config['opendns']['host'] = $pconfig['host']; - if ($config['opendns']['standalone']) { - /* nothing to do, keep system state */ - } elseif ($config['opendns']['enable']) { - $config['system']['dnsserver'] = array(); - $v4_server = array('208.67.222.222', '208.67.220.220'); - $v6_server = array('2620:119:35::35', '2620:119:53::53'); - if (isset($config['system']['prefer_ipv4'])) { - $config['system']['dnsserver'][] = $v4_server[0]; - $config['system']['dnsserver'][] = $v4_server[1]; - if (is_ipv6_allowed()) { - $config['system']['dnsserver'][] = $v6_server[0]; - $config['system']['dnsserver'][] = $v6_server[1]; - } - } else { - if (is_ipv6_allowed()) { - $config['system']['dnsserver'][] = $v6_server[0]; - $config['system']['dnsserver'][] = $v6_server[1]; - } - $config['system']['dnsserver'][] = $v4_server[0]; - $config['system']['dnsserver'][] = $v4_server[1]; - } - $config['system']['dnsallowoverride'] = '0'; - } else { - $config['system']['dnsserver'] = []; - $config['system']['dnsserver'][] = ''; - $config['system']['dnsallowoverride'] = '1'; - } - write_config('OpenDNS filter configuration change'); - system_resolver_configure(); - plugins_configure('dhcp'); - $savemsg = get_std_save_message(); - } -} - -legacy_html_escape_form_data($pconfig); - -include 'head.inc'; - -?> - - - -
-
-
- 0) { - print_input_errors($input_errors); - } - if (isset($savemsg)) { - print_info_box($savemsg); - }?> -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -   -
- /> - - -
- /> - - -
- - -
- -
- - -
- %s
', - strpos($result, 'good') === 0 ? 'check text-success' : 'times text-danger', - $result - ); - }?> -
  - - -
-
-
-
-
-
-
- -