From 1fa69c27ee153fe439c2ba9a9809a28e452811ea Mon Sep 17 00:00:00 2001 From: jim-p Date: Fri, 6 Apr 2018 13:56:31 -0400 Subject: [PATCH] Add GUI controls to the DNS Resolver for providing DNS over TLS service to local clients. Implements #8030 --- src/etc/inc/unbound.inc | 87 +++++++++++++++++--------- src/usr/local/www/services_unbound.php | 61 +++++++++++++++++- 2 files changed, 118 insertions(+), 30 deletions(-) diff --git a/src/etc/inc/unbound.inc b/src/etc/inc/unbound.inc index a80afb9347..3bee4730c0 100644 --- a/src/etc/inc/unbound.inc +++ b/src/etc/inc/unbound.inc @@ -178,35 +178,64 @@ private-address: fe80::/10 EOF; } - // Determine interfaces to run on - $bindints = ""; - if (!empty($unboundcfg['active_interface'])) { - $active_interfaces = explode(",", $unboundcfg['active_interface']); - if (in_array("all", $active_interfaces, true)) { - $bindints .= "interface: 0.0.0.0\n"; - $bindints .= "interface: ::0\n"; - $bindints .= "interface-automatic: yes\n"; - } else { - foreach ($active_interfaces as $ubif) { - if (is_ipaddr($ubif)) { - $bindints .= "interface: $ubif\n"; - } else { - $intip = get_interface_ip($ubif); - if (is_ipaddrv4($intip)) { - $bindints .= "interface: $intip\n"; - } - $intip = get_interface_ipv6($ubif); - if (is_ipaddrv6($intip)) { - $bindints .= "interface: $intip\n"; - } + // Determine interfaces where unbound will bind + $sslport = is_numeric($unboundcfg['sslport']) ? $unboundcfg['sslport'] : "853"; + $bindintcfg = ""; + $bindints = array(); + $active_interfaces = explode(",", $unboundcfg['active_interface']); + if (empty($unboundcfg['active_interface']) || in_array("all", $active_interfaces, true)) { + $bindints[] = "0.0.0.0"; + $bindints[] = "::0"; + $bindintcfg .= "interface-automatic: " . (isset($unboundcfg['enablessl']) ? "no" : "yes") . "\n"; + } else { + foreach ($active_interfaces as $ubif) { + if (is_ipaddr($ubif)) { + $bindints[] = $ubif; + } else { + $intip = get_interface_ip($ubif); + if (is_ipaddrv4($intip)) { + $bindints[] = $intip; + } + $intip = get_interface_ipv6($ubif); + if (is_ipaddrv6($intip)) { + $bindints[] = $intip; } } } - } else { - $bindints .= "interface: 0.0.0.0\n"; - $bindints .= "interface: ::0\n"; - /* If the active interface array is empty, treat it the same as "All" as is done above. Otherwise it breaks CARP with a default config. */ - $bindints .= "interface-automatic: yes\n"; + } + foreach ($bindints as $bindint) { + $bindintcfg .= "interface: {$bindint}\n"; + if (isset($unboundcfg['enablessl'])) { + $bindintcfg .= "interface: {$bindint}@{$sslport}\n"; + } + } + + // SSL Configuration + $sslconfig = ""; + if (isset($unboundcfg['enablessl'])) { + $sslcert_path = "{$g['unbound_chroot_path']}/sslcert.crt"; + $sslkey_path = "{$g['unbound_chroot_path']}/sslcert.key"; + + // Enable SSL on the chosen or default port + $sslconfig .= "ssl-port: {$sslport}\n"; + + // Lookup CA and Server Cert + $cert = lookup_cert($unboundcfg['sslcertref']); + $ca = ca_chain($cert); + $cert_chain = base64_decode($cert['crt']); + if (!empty($ca)) { + $cert_chain .= "\n" . $ca; + } + + // Write CA and Server Cert + file_put_contents($sslcert_path, $cert_chain); + chmod($sslcert_path, 0644); + file_put_contents($sslkey_path, base64_decode($cert['prv'])); + chmod($sslkey_path, 0600); + + // Add config for CA and Server Cert + $sslconfig .= "ssl-service-pem: \"{$sslcert_path}\"\n"; + $sslconfig .= "ssl-service-key: \"{$sslkey_path}\"\n"; } // Determine interfaces to run on @@ -392,10 +421,11 @@ use-caps-for-id: {$use_caps} serve-expired: {$dns_record_cache} # Statistics {$statistics} +# SSL Configuration +{$sslconfig} # Interface IP(s) to bind to -{$bindints} +{$bindintcfg} {$outgoingints} - # DNS Rebinding {$private_addr} {$private_domains} @@ -764,7 +794,6 @@ function unbound_acls_config($cfgsubdir = "") { $active_interfaces = get_configured_interface_with_descr(); } - $bindints = ""; foreach ($active_interfaces as $ubif => $ifdesc) { $ifip = get_interface_ip($ubif); if (is_ipaddrv4($ifip)) { diff --git a/src/usr/local/www/services_unbound.php b/src/usr/local/www/services_unbound.php index 5f00277151..8736277558 100644 --- a/src/usr/local/www/services_unbound.php +++ b/src/usr/local/www/services_unbound.php @@ -53,6 +53,9 @@ $a_domainOverrides = &$a_unboundcfg['domainoverrides']; if (isset($a_unboundcfg['enable'])) { $pconfig['enable'] = true; } +if (isset($a_unboundcfg['enablessl'])) { + $pconfig['enablessl'] = true; +} if (isset($a_unboundcfg['dnssec'])) { $pconfig['dnssec'] = true; } @@ -73,6 +76,8 @@ if (isset($a_unboundcfg['regovpnclients'])) { } $pconfig['port'] = $a_unboundcfg['port']; +$pconfig['sslport'] = $a_unboundcfg['sslport']; +$pconfig['sslcertref'] = $a_unboundcfg['sslcertref']; $pconfig['custom_options'] = base64_decode($a_unboundcfg['custom_options']); if (empty($a_unboundcfg['active_interface'])) { @@ -93,6 +98,14 @@ if (empty($a_unboundcfg['system_domain_local_zone_type'])) { $pconfig['system_domain_local_zone_type'] = $a_unboundcfg['system_domain_local_zone_type']; } +$a_cert =& $config['cert']; +$certs_available = false; + +if (is_array($a_cert) && count($a_cert)) { + $certs_available = true; +} else { + $a_cert = array(); +} if ($_POST['apply']) { $retval = 0; @@ -116,6 +129,10 @@ if ($_POST['save']) { } } + if (isset($pconfig['enablessl']) && (!$certs_available || empty($pconfig['sslcertref']))) { + $input_errors[] = gettext("Acting as an SSL/TLS server requires a valid server certificate"); + } + // forwarding mode requires having valid DNS servers if (isset($pconfig['forwarding'])) { $founddns = false; @@ -154,6 +171,9 @@ if ($_POST['save']) { if ($pconfig['port'] && !is_port($pconfig['port'])) { $input_errors[] = gettext("A valid port number must be specified."); } + if ($pconfig['sslport'] && !is_port($pconfig['sslport'])) { + $input_errors[] = gettext("A valid SSL/TLS port number must be specified."); + } if (is_array($pconfig['active_interface']) && !empty($pconfig['active_interface'])) { $display_active_interface = $pconfig['active_interface']; @@ -184,7 +204,10 @@ if ($_POST['save']) { if (!$input_errors) { $a_unboundcfg['enable'] = isset($pconfig['enable']); + $a_unboundcfg['enablessl'] = isset($pconfig['enablessl']); $a_unboundcfg['port'] = $pconfig['port']; + $a_unboundcfg['sslport'] = $pconfig['sslport']; + $a_unboundcfg['sslcertref'] = $pconfig['sslcertref']; $a_unboundcfg['dnssec'] = isset($pconfig['dnssec']); $a_unboundcfg['forwarding'] = isset($pconfig['forwarding']); $a_unboundcfg['forward_tls_upstream'] = isset($pconfig['forward_tls_upstream']); @@ -297,6 +320,42 @@ $section->addInput(new Form_Input( ['placeholder' => '53'] ))->setHelp('The port used for responding to DNS queries. It should normally be left blank unless another service needs to bind to TCP/UDP port 53.'); +$section->addInput(new Form_Checkbox( + 'enablessl', + 'Enable SSL/TLS Service', + 'Respond to incoming SSL/TLS queries from local clients', + $pconfig['enablessl'] +))->setHelp('Configures the DNS Resolver to act as a DNS over SSL/TLS server which can answer queries from clients which also support DNS over TLS. ' . + 'Activating this option disables automatic interface response routing behavior, thus it works best with specific interface bindings.' ); + +if ($certs_available) { + $values = array(); + foreach ($a_cert as $cert) { + $values[ $cert['refid'] ] = $cert['descr']; + } + + $section->addInput($input = new Form_Select( + 'sslcertref', + 'SSL/TLS Certificate', + $pconfig['sslcertref'], + $values + ))->setHelp('The server certificate to use for SSL/TLS service. The CA chain will be determined automatically.'); +} else { + $section->addInput(new Form_StaticText( + 'SSL/TLS Certificate', + sprintf('No Certificates have been defined. A certificate is required before SSL/TLS can be enabled. %1$s Create or Import %2$s a Certificate.', + '', '') + )); +} + +$section->addInput(new Form_Input( + 'sslport', + 'SSL/TLS Listen Port', + 'number', + $pconfig['sslport'], + ['placeholder' => '853'] +))->setHelp('The port used for responding to SSL/TLS DNS queries. It should normally be left blank unless another service needs to bind to TCP/UDP port 853.'); + $activeiflist = build_if_list($pconfig['active_interface']); $section->addInput(new Form_Select( @@ -344,7 +403,7 @@ $section->addInput(new Form_Checkbox( $section->addInput(new Form_Checkbox( 'forward_tls_upstream', null, - 'Use SSL/TLS for DNS Queries to Forwarding Servers', + 'Use SSL/TLS for outgoing DNS Queries to Forwarding Servers', $pconfig['forward_tls_upstream'] ))->setHelp('When set in conjunction with DNS Query Forwarding, queries to all upstream forwarding DNS servers will be sent using SSL/TLS on the default port of 853. Note that ALL configured forwarding servers MUST support SSL/TLS queries on port 853.');