Skip to content

Commit

Permalink
unbound: restrict creation of PTR records for both the system domain …
Browse files Browse the repository at this point in the history
…and host overrides (#5925)

In order to prevent the unpredictable behaviour of random PTR records being returned, which is not explicitly prohibited in RFC1035, it is best to restrict the creation of PTR records from every single host and alias (except for wildcard entries, no PTR records are created here), to only non-alias overrides (edit: the exception here is an alias whose parent does not create a PTR record, a wildcard entry). We also further restrict it to unique IP addresses so there can be no confusion in how to maintain the entries within the running Unbound instance.

Hopefully this can pave the way for adding PTR records as a separate type instead of generating them under the hood, as is done currently.

This change should at least address inconsistencies regarding random PTR records being returned as mentioned in #5477

A slight refactor of the existing unbound code is also included here for code reduction purposes.

(cherry picked from commit 92a5a22)
  • Loading branch information
swhite2 authored and fichtner committed Aug 24, 2022
1 parent f217356 commit 547c8b1
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 33 deletions.
83 changes: 50 additions & 33 deletions src/etc/inc/plugins.inc.d/unbound.inc
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,9 @@ function unbound_add_host_entries($ifconfig_details = null)
global $config;

$local_zone_type = 'transparent';
$ptr_records = ['127.0.0.1', '::1'];

openlog("unbound", LOG_DAEMON, LOG_LOCAL4);

if (!empty($config['unbound']['local_zone_type'])) {
$local_zone_type = $config['unbound']['local_zone_type'];
Expand All @@ -462,42 +465,46 @@ function unbound_add_host_entries($ifconfig_details = null)
} else {
$interfaces = array_keys(get_configured_interface_with_descr());
}
foreach ($interfaces as $interface) {
if ($interface == 'lo0' || substr($interface, 0, 4) == 'ovpn') {
continue;
}

list ($laddr) = interfaces_primary_address($interface, $ifconfig_details);
if (!empty($laddr)) {
$domain = $config['system']['domain'];
if (isset($config['dhcpd'][$interface]['enable']) && !empty($config['dhcpd'][$interface]['domain'])) {
$domain = $config['dhcpd'][$interface]['domain'];
}
$unbound_entries .= "local-data-ptr: \"{$laddr} {$config['system']['hostname']}.{$domain}\"\n";
$unbound_entries .= "local-data: \"{$config['system']['hostname']}.{$domain} A {$laddr}\"\n";
$unbound_entries .= "local-data: \"{$config['system']['hostname']} A {$laddr}\"\n";
}
list ($laddr6) = interfaces_primary_address6($interface, $ifconfig_details);
if (!empty($laddr6)) {
$domain = $config['system']['domain'];
if (isset($config['dhcpdv6'][$interface]['enable']) && !empty($config['dhcpdv6'][$interface]['domain'])) {
$domain = $config['dhcpdv6'][$interface]['domain'];
if (empty($config['unbound']['noregrecords'])) {
foreach ($interfaces as $interface) {
if ($interface == 'lo0' || substr($interface, 0, 4) == 'ovpn') {
continue;
}
$unbound_entries .= "local-data-ptr: \"{$laddr6} {$config['system']['hostname']}.{$domain}\"\n";
$unbound_entries .= "local-data: \"{$config['system']['hostname']}.{$domain} AAAA {$laddr6}\"\n";
$unbound_entries .= "local-data: \"{$config['system']['hostname']} AAAA {$laddr6}\"\n";
}
if (empty($config['unbound']['noreglladdr6'])) {
list ($lladdr6) = interfaces_scoped_address6($interface, $ifconfig_details);
if (!empty($lladdr6)) {
/* cannot embed scope */
$lladdr6 = explode('%', $lladdr6)[0];

list ($laddr) = interfaces_primary_address($interface, $ifconfig_details);
list ($laddr6) = interfaces_primary_address6($interface, $ifconfig_details);

foreach (['4' => $laddr, '6' => $laddr6] as $ip_version => $addr) {
if (empty($addr)) {
continue;
}

$domain = $config['system']['domain'];
if (isset($config['dhcpdv6'][$interface]['enable']) && !empty($config['dhcpdv6'][$interface]['domain'])) {
$domain = $config['dhcpdv6'][$interface]['domain'];
$dhcpd = $ip_version == '4' ? 'dhcpd' : 'dhcpd6';
$record = $ip_version == '4' ? 'A' : 'AAAA';
if (isset($config[$dhcpd][$interface]['enable']) && !empty($config[$dhcpd][$interface]['domain'])) {
$domain = $config[$dhcpd][$interface]['domain'];
}
if ($interface === get_primary_interface_from_list($interfaces)) {
$unbound_entries .= "local-data-ptr: \"{$addr} {$config['system']['hostname']}.{$domain}\"\n";
$ptr_records[] = $addr;
}
$unbound_entries .= "local-data: \"{$config['system']['hostname']}.{$domain} {$record} {$addr}\"\n";
$unbound_entries .= "local-data: \"{$config['system']['hostname']} {$record} {$addr}\"\n";
}

if (empty($config['unbound']['noreglladdr6'])) {
if (!empty($lladdr6)) {
/* cannot embed scope */
$lladdr6 = explode('%', $lladdr6)[0];
$domain = $config['system']['domain'];
if (isset($config['dhcpdv6'][$interface]['enable']) && !empty($config['dhcpdv6'][$interface]['domain'])) {
$domain = $config['dhcpdv6'][$interface]['domain'];
}
$unbound_entries .= "local-data: \"{$config['system']['hostname']}.{$domain} AAAA {$lladdr6}\"\n";
$unbound_entries .= "local-data: \"{$config['system']['hostname']} AAAA {$lladdr6}\"\n";
}
$unbound_entries .= "local-data: \"{$config['system']['hostname']}.{$domain} AAAA {$lladdr6}\"\n";
$unbound_entries .= "local-data: \"{$config['system']['hostname']} AAAA {$lladdr6}\"\n";
}
}
}
Expand Down Expand Up @@ -563,7 +570,15 @@ function unbound_add_host_entries($ifconfig_details = null)
$unbound_entries .= "local-zone: \"{$alias['domain']}\" redirect\n";
$unbound_entries .= "local-data: \"{$alias['domain']} IN {$host->rr} {$host->server}\"\n";
} else {
$unbound_entries .= "local-data-ptr: \"{$host->server} {$alias['hostname']}{$alias['domain']}\"\n";
if (($alias === $tmp_aliases[0] || $tmp_aliases[0]['hostname'] === '*') && !in_array($host->server, $ptr_records, true)) {
/* Only generate a PTR record for the non-alias override and only if the IP is not already associated with a PTR.
* The exception to this is an alias whose parent uses a wildcard and as such does not specify a PTR record.
*/
$unbound_entries .= "local-data-ptr: \"{$host->server} {$alias['hostname']}{$alias['domain']}\"\n";
$ptr_records[] = $host->server;
} else {
syslog(LOG_WARNING, 'PTR record already exists for ' . $alias['hostname'] . $alias['domain'] . '(' . $host->server . ')');
}
$unbound_entries .= "local-data: \"{$alias['hostname']}{$alias['domain']} IN {$host->rr} {$host->server}\"\n";
}
break;
Expand Down Expand Up @@ -602,6 +617,8 @@ function unbound_add_host_entries($ifconfig_details = null)
}

file_put_contents('/var/unbound/host_entries.conf', $unbound_entries);

closelog();
}

function unbound_acls_subnets()
Expand Down
16 changes: 16 additions & 0 deletions src/www/services_unbound.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
$pconfig['regdhcpstatic'] = isset($a_unboundcfg['regdhcpstatic']);
$pconfig['txtsupport'] = isset($a_unboundcfg['txtsupport']);
$pconfig['cacheflush'] = isset($a_unboundcfg['cacheflush']);
$pconfig['noregrecords'] = isset($a_unboundcfg['noregrecords']);
// text values
$pconfig['port'] = !empty($a_unboundcfg['port']) ? $a_unboundcfg['port'] : null;
$pconfig['regdhcpdomain'] = !empty($a_unboundcfg['regdhcpdomain']) ? $a_unboundcfg['regdhcpdomain'] : null;
Expand Down Expand Up @@ -112,6 +113,7 @@
}

// boolean values
$a_unboundcfg['noregrecords'] = !empty($pconfig['noregrecords']);
$a_unboundcfg['cacheflush'] = !empty($pconfig['cacheflush']);
$a_unboundcfg['dns64'] = !empty($pconfig['dns64']);
$a_unboundcfg['noarecords'] = !empty($pconfig['noarecords']);
Expand Down Expand Up @@ -303,6 +305,20 @@
</div>
</td>
</tr>
<tr>
<td><a id="help_for_noregrecords" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?= gettext('System A/AAAA records') ?></td>
<td>
<input name="noregrecords" type="checkbox" id="noregrecords" value="yes" <?= !empty($pconfig['noregrecords']) ? 'checked="checked"' : '' ?>/>
<?= gettext('Do not register system A/AAAA records') ?>
<div class="hidden" data-for="help_for_noregrecords">
<?= sprintf(gettext("If this option is set, then no A/AAAA records for " .
"the configured listen interfaces will be generated. " .
"If desired, you can manually add them in %sUnbound DNS: Overrides%s. " .
"Use this to control which interface IP addresses are mapped to the system host/domain name " .
"as well as to restrict the amount of information exposed in replies to queries for the system host/domain name ."), '<a href="ui/unbound/overrides/">', '</a>'); ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_txtsupport" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("TXT Comment Support");?></td>
<td>
Expand Down

0 comments on commit 547c8b1

Please sign in to comment.