Merge pull request #706 from makuser/master

Implement CAA DNS records
This commit is contained in:
Michael Kaufmann
2019-07-30 09:53:46 +02:00
committed by GitHub
11 changed files with 491 additions and 29 deletions

View File

@@ -138,6 +138,43 @@ class DomainZones extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour
$errors[] = $this->lng['error']['dns_arec_noipv4'];
} elseif ($type == 'AAAA' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
$errors[] = $this->lng['error']['dns_aaaarec_noipv6'];
} elseif ($type == 'CAA' && ! empty($content)) {
$re = '/(?\'critical\'\d)\h*(?\'type\'iodef|issue|issuewild)\h*(?\'value\'(?\'issuevalue\'"(?\'domain\'(?=.{3,128}$)(?>(?>[a-zA-Z0-9]+[a-zA-Z0-9-]*[a-zA-Z0-9]+|[a-zA-Z0-9]+)\.)*(?>[a-zA-Z]{2,}|[a-zA-Z0-9]{2,}\.[a-zA-Z]{2,}))[;\h]*(?\'parameters\'(?>[a-zA-Z0-9]{1,60}=[a-zA-Z0-9]{1,60}\h*)+)?")|(?\'iodefvalue\'"(?\'url\'(mailto:.*|http:\/\/.*|https:\/\/.*))"))/';
preg_match($re, $content, $matches);
if (empty($matches)) {
$errors[] = $this->lng['error']['dns_content_invalid'];
} elseif (($matches['type'] == 'issue' || $matches['type'] == 'issuewild') && !\Froxlor\Validate\Validate::validateDomain($matches['domain'])) {
$errors[] = $this->lng['error']['dns_content_invalid'];
} elseif ($matches['type'] == 'iodef' && !\Froxlor\Validate\Validate::validateUrl($matches['url'])) {
$errors[] = $this->lng['error']['dns_content_invalid'];
} else {
$content = $matches[0];
}
} elseif ($type == 'CNAME' || $type == 'DNAME') {
// check for trailing dot
if (substr($content, - 1) == '.') {
// remove it for checks
$content = substr($content, 0, - 1);
} else {
// add domain name
$content .= '.' . $domain;
}
if (! \Froxlor\Validate\Validate::validateDomain($content, true)) {
$errors[] = $this->lng['error']['dns_cname_invaliddom'];
} else {
// check whether there are RR-records for the same resource
foreach ($dom_entries as $existing_entries) {
if (($existing_entries['type'] == 'A' || $existing_entries['type'] == 'AAAA' || $existing_entries['type'] == 'MX' || $existing_entries['type'] == 'NS') && $existing_entries['record'] == $record) {
$errors[] = $this->lng['error']['dns_cname_nomorerr'];
break;
}
}
}
// append trailing dot (again)
$content .= '.';
} elseif ($type == 'LOC' && ! empty($content)) {
$content = $content;
} elseif ($type == 'MX') {
if ($prio === null || $prio < 0) {
$errors[] = $this->lng['error']['dns_mx_prioempty'];
@@ -161,28 +198,6 @@ class DomainZones extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour
}
// append trailing dot (again)
$content .= '.';
} elseif ($type == 'CNAME') {
// check for trailing dot
if (substr($content, - 1) == '.') {
// remove it for checks
$content = substr($content, 0, - 1);
} else {
// add domain name
$content .= '.' . $domain;
}
if (! \Froxlor\Validate\Validate::validateDomain($content, true)) {
$errors[] = $this->lng['error']['dns_cname_invaliddom'];
} else {
// check whether there are RR-records for the same resource
foreach ($dom_entries as $existing_entries) {
if (($existing_entries['type'] == 'A' || $existing_entries['type'] == 'AAAA' || $existing_entries['type'] == 'MX' || $existing_entries['type'] == 'NS') && $existing_entries['record'] == $record) {
$errors[] = $this->lng['error']['dns_cname_nomorerr'];
break;
}
}
}
// append trailing dot (again)
$content .= '.';
} elseif ($type == 'NS') {
// check for trailing dot
if (substr($content, - 1) == '.') {
@@ -194,9 +209,8 @@ class DomainZones extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour
}
// append trailing dot (again)
$content .= '.';
} elseif ($type == 'TXT' && ! empty($content)) {
// check that TXT content is enclosed in " "
$content = \Froxlor\Dns\Dns::encloseTXTContent($content);
} elseif ($type == 'RP' && ! empty($content)) {
$content = $content;
} elseif ($type == 'SRV') {
if ($prio === null || $prio < 0) {
$errors[] = $this->lng['error']['dns_srv_prioempty'];
@@ -232,6 +246,11 @@ class DomainZones extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour
if (substr($content, - 1) != '.') {
$content .= '.';
}
} elseif ($type == 'SSHFP' && ! empty($content)) {
$content = $content;
} elseif ($type == 'TXT' && ! empty($content)) {
// check that TXT content is enclosed in " "
$content = \Froxlor\Dns\Dns::encloseTXTContent($content);
}
$new_entry = array(

View File

@@ -130,6 +130,12 @@ class Dns
}
}
// additional required records for CAA if activated
if (Settings::Get('system.dns_createcaaentry') && Settings::Get('system.use_ssl') == "1" && !empty($domain['p_ssl_ipandports'])) {
// check for CAA content later
self::addRequiredEntry('@CAA@', 'CAA', $required_entries);
}
// additional required records for SPF and DKIM if activated
if ($domain['isemaildomain'] == '1') {
if (Settings::Get('spf.use_spf') == '1') {
@@ -150,6 +156,10 @@ class Dns
if (array_key_exists($entry['type'], $required_entries) && array_key_exists(md5($entry['record']), $required_entries[$entry['type']])) {
unset($required_entries[$entry['type']][md5($entry['record'])]);
}
if (Settings::Get('system.dns_createcaaentry') == '1' && $entry['type'] == 'CAA' && strtolower(substr($entry['content'], 0, 7)) == '"v=caa1') {
// unset special CAA required-entry
unset($required_entries[$entry['type']][md5("@CAA@")]);
}
if (Settings::Get('spf.use_spf') == '1' && $entry['type'] == 'TXT' && $entry['record'] == '@' && strtolower(substr($entry['content'], 0, 7)) == '"v=spf1') {
// unset special spf required-entry
unset($required_entries[$entry['type']][md5("@SPF@")]);
@@ -278,6 +288,31 @@ class Dns
}
}
}
// CAA
if (array_key_exists("CAA", $required_entries)) {
foreach ($required_entries as $type => $records) {
if ($type == 'CAA') {
foreach ($records as $record) {
if ($record == '@CAA@') {
$caa_entries = explode(PHP_EOL, Settings::Get('caa.caa_entry'));
if ($domain['letsencrypt'] == 1) {
$le_entry = $domain['iswildcarddomain'] == '1' ? '0 issuewild "letsencrypt.org"' : '0 issue "letsencrypt.org"';
array_push($caa_entries, $le_entry);
}
foreach ($caa_entries as $entry) {
$zonerecords[] = new DnsEntry('@', 'CAA', $entry);
// additional required records by subdomain setting
if ($domain['wwwserveralias'] == '1') {
$zonerecords[] = new DnsEntry('www', 'CAA', $entry);
}
}
}
}
}
}
}
}
if (empty($primary_ns)) {

View File

@@ -10,7 +10,7 @@ final class Froxlor
const VERSION = '0.10.0-rc2';
// Database version (YYYYMMDDC where C is a daily counter)
const DBVERSION = '201904250';
const DBVERSION = '201907270';
// Distribution branding-tag (used for Debian etc.)
const BRANDING = '';