mirror of
https://github.com/Froxlor/Froxlor.git
synced 2024-09-21 10:27:29 +00:00
Ensure that DMARC entries are generated as subdomain, Allow overwriting of DMARC and SPF subdomain records (#1237)
* Ensure that DMARC entries are generated as subdomain - see https://datatracker.ietf.org/doc/html/rfc7489#section-6.1 * Add tests for DNS DMARC * Allow custom SPF and DMARC subdomain records to replace default records * Improve tests for DMARC, add DMARC tests for subdomain
This commit is contained in:
parent
953baec023
commit
686ca84a30
@ -212,7 +212,7 @@ class Dns
|
|||||||
}
|
}
|
||||||
if (Settings::Get('dmarc.use_dmarc') == '1') {
|
if (Settings::Get('dmarc.use_dmarc') == '1') {
|
||||||
// check for DMARC content later
|
// check for DMARC content later
|
||||||
self::addRequiredEntry('@DMARC@.' . $sub_record, 'TXT', $required_entries);
|
self::addRequiredEntry('@DMARC@', 'TXT', $required_entries);
|
||||||
}
|
}
|
||||||
if (Settings::Get('antispam.activated') == '1' && $domain['dkim'] == '1') {
|
if (Settings::Get('antispam.activated') == '1' && $domain['dkim'] == '1') {
|
||||||
// check for DKIM content later
|
// check for DKIM content later
|
||||||
@ -239,19 +239,28 @@ class Dns
|
|||||||
}
|
}
|
||||||
if (Settings::Get('spf.use_spf') == '1'
|
if (Settings::Get('spf.use_spf') == '1'
|
||||||
&& $entry['type'] == 'TXT'
|
&& $entry['type'] == 'TXT'
|
||||||
&& $entry['record'] == '@'
|
|
||||||
&& (strtolower(substr($entry['content'], 0, 7)) == '"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1')
|
&& (strtolower(substr($entry['content'], 0, 7)) == '"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1')
|
||||||
) {
|
) {
|
||||||
// unset special spf required-entry
|
// unset special spf required-entry
|
||||||
unset($required_entries[$entry['type']][md5("@SPF@")]);
|
if ($entry['record'] == '@') {
|
||||||
|
unset($required_entries[$entry['type']][md5("@SPF@")]);
|
||||||
|
} else {
|
||||||
|
// subdomain
|
||||||
|
unset($required_entries[$entry['type']][md5("@SPF@." . $entry['record'])]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Settings::Get('dmarc.use_dmarc') == '1'
|
if (Settings::Get('dmarc.use_dmarc') == '1'
|
||||||
&& $entry['type'] == 'TXT'
|
&& $entry['type'] == 'TXT'
|
||||||
&& $entry['record'] == '@'
|
&& ($entry['record'] == '_dmarc' || substr($entry['record'], 0, 7) == '_dmarc.')
|
||||||
&& (strtolower(substr($entry['content'], 0, 9)) == '"v=dmarc1' || strtolower(substr($entry['content'], 0, 8)) == 'v=dmarc1')
|
&& (strtolower(substr($entry['content'], 0, 9)) == '"v=dmarc1' || strtolower(substr($entry['content'], 0, 8)) == 'v=dmarc1')
|
||||||
) {
|
) {
|
||||||
// unset special dmarc required-entry
|
// unset special dmarc required-entry
|
||||||
unset($required_entries[$entry['type']][md5("@DMARC@")]);
|
if ($entry['record'] == '_dmarc') {
|
||||||
|
unset($required_entries[$entry['type']][md5("@DMARC@")]);
|
||||||
|
} else {
|
||||||
|
// subdomain
|
||||||
|
unset($required_entries[$entry['type']][md5("@DMARC@" . substr($entry['record'], 6))]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (empty($primary_ns) && $entry['record'] == '@' && $entry['type'] == 'NS') {
|
if (empty($primary_ns) && $entry['record'] == '@' && $entry['type'] == 'NS') {
|
||||||
// use the first NS entry pertaining to the current domain as primary ns
|
// use the first NS entry pertaining to the current domain as primary ns
|
||||||
@ -392,12 +401,12 @@ class Dns
|
|||||||
} elseif ($record == '@DMARC@') {
|
} elseif ($record == '@DMARC@') {
|
||||||
// dmarc for main-domain
|
// dmarc for main-domain
|
||||||
$txt_content = Settings::Get('dmarc.dmarc_entry');
|
$txt_content = Settings::Get('dmarc.dmarc_entry');
|
||||||
$zonerecords[] = new DnsEntry('@', 'TXT', self::encloseTXTContent($txt_content));
|
$zonerecords[] = new DnsEntry('_dmarc', 'TXT', self::encloseTXTContent($txt_content));
|
||||||
} elseif (strlen($record) > 8 && substr($record, 0, 8) == '@DMARC@.') {
|
} elseif (strlen($record) > 8 && substr($record, 0, 8) == '@DMARC@.') {
|
||||||
// dmarc for subdomain
|
// dmarc for subdomain
|
||||||
$txt_content = Settings::Get('dmarc.dmarc_entry');
|
$txt_content = Settings::Get('dmarc.dmarc_entry');
|
||||||
$sub_record = substr($record, 8);
|
$sub_record = substr($record, 8);
|
||||||
$zonerecords[] = new DnsEntry($sub_record, 'TXT', self::encloseTXTContent($txt_content));
|
$zonerecords[] = new DnsEntry('_dmarc.' . $sub_record, 'TXT', self::encloseTXTContent($txt_content));
|
||||||
} elseif (!empty($dkim_entries)) {
|
} elseif (!empty($dkim_entries)) {
|
||||||
// DKIM entries
|
// DKIM entries
|
||||||
$dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey';
|
$dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey';
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
use Froxlor\Settings;
|
use Froxlor\Settings;
|
||||||
|
use Froxlor\Api\Commands\Admins;
|
||||||
use Froxlor\Api\Commands\Customers;
|
use Froxlor\Api\Commands\Customers;
|
||||||
use Froxlor\Api\Commands\DomainZones;
|
use Froxlor\Api\Commands\DomainZones;
|
||||||
use Froxlor\Api\Commands\Domains;
|
use Froxlor\Api\Commands\Domains;
|
||||||
|
use Froxlor\Api\Commands\SubDomains;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -63,6 +65,89 @@ class DomainZonesTest extends TestCase
|
|||||||
DomainZones::getLocal($customer_userdata, $data)->get();
|
DomainZones::getLocal($customer_userdata, $data)->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @depends testCustomerDomainZonesGet
|
||||||
|
*/
|
||||||
|
public function testCustomerDomainZonesGetWithDMARC()
|
||||||
|
{
|
||||||
|
global $admin_userdata;
|
||||||
|
|
||||||
|
Settings::Set('dmarc.use_dmarc', 1, true);
|
||||||
|
|
||||||
|
// get customer
|
||||||
|
$json_result = Customers::getLocal($admin_userdata, array(
|
||||||
|
'loginname' => 'test1'
|
||||||
|
))->get();
|
||||||
|
$customer_userdata = json_decode($json_result, true)['data'];
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'domainname' => 'test2.local'
|
||||||
|
];
|
||||||
|
$json_result = DomainZones::getLocal($customer_userdata, $data)->get();
|
||||||
|
$result = json_decode($json_result, true)['data'];
|
||||||
|
$this->assertTrue(count($result) > 1);
|
||||||
|
$foundCnt = 0;
|
||||||
|
$foundStr = '';
|
||||||
|
foreach ($result as $entry) {
|
||||||
|
if (substr($entry, 0, 7) == '_dmarc ') {
|
||||||
|
$foundCnt++;
|
||||||
|
$foundStr = $entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->assertEquals(1, $foundCnt);
|
||||||
|
$resstr = preg_replace('/\s+/', '', $foundStr);
|
||||||
|
$against = preg_replace('/\s+/', '', '_dmarc 604800 IN TXT v=DMARC1;p=none;');
|
||||||
|
$this->assertEquals($against, $resstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @depends testCustomerDomainZonesGetWithDMARC
|
||||||
|
*/
|
||||||
|
public function testCustomerDomainZonesGetWithDMARCSubdomain()
|
||||||
|
{
|
||||||
|
global $admin_userdata;
|
||||||
|
|
||||||
|
// enable isemaildomain for subdomain
|
||||||
|
$json_result = Admins::getLocal($admin_userdata, array(
|
||||||
|
'loginname' => 'reseller'
|
||||||
|
))->get();
|
||||||
|
$reseller_userdata = json_decode($json_result, true)['data'];
|
||||||
|
$reseller_userdata['adminsession'] = 1;
|
||||||
|
$data = [
|
||||||
|
'domainname' => 'mysub2.test2.local',
|
||||||
|
'isemaildomain' => 1,
|
||||||
|
'customerid' => 1
|
||||||
|
];
|
||||||
|
$json_result = SubDomains::getLocal($reseller_userdata, $data)->update();
|
||||||
|
|
||||||
|
// get customer
|
||||||
|
$json_result = Customers::getLocal($admin_userdata, array(
|
||||||
|
'loginname' => 'test1'
|
||||||
|
))->get();
|
||||||
|
$customer_userdata = json_decode($json_result, true)['data'];
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'domainname' => 'test2.local'
|
||||||
|
];
|
||||||
|
$json_result = DomainZones::getLocal($customer_userdata, $data)->get();
|
||||||
|
$result = json_decode($json_result, true)['data'];
|
||||||
|
$foundCnt = 0;
|
||||||
|
$foundStr = '';
|
||||||
|
$this->assertTrue(count($result) > 1);
|
||||||
|
foreach ($result as $entry) {
|
||||||
|
if (substr($entry, 0, 14) == '_dmarc.mysub2 ') {
|
||||||
|
$foundCnt++;
|
||||||
|
$foundStr = $entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->assertEquals(1, $foundCnt);
|
||||||
|
$resstr = preg_replace('/\s+/', '', $foundStr);
|
||||||
|
$against = preg_replace('/\s+/', '', '_dmarc.mysub2 604800 IN TXT v=DMARC1;p=none;');
|
||||||
|
$this->assertEquals($against, $resstr);
|
||||||
|
}
|
||||||
|
|
||||||
public function testAdminDomainZonesUpdate()
|
public function testAdminDomainZonesUpdate()
|
||||||
{
|
{
|
||||||
global $admin_userdata;
|
global $admin_userdata;
|
||||||
@ -872,6 +957,68 @@ class DomainZonesTest extends TestCase
|
|||||||
$this->assertEquals('_test1 18000 IN TXT aw yeah', $entry);
|
$this->assertEquals('_test1 18000 IN TXT aw yeah', $entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @depends testCustomerDomainZonesGetWithDMARC
|
||||||
|
*/
|
||||||
|
public function testAdminDomainZonesAddTXTCustomDMARC()
|
||||||
|
{
|
||||||
|
global $admin_userdata;
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'domainname' => 'test2.local',
|
||||||
|
'record' => '_dmarc',
|
||||||
|
'type' => 'TXT',
|
||||||
|
'content' => 'v=DMARC1;p=none;overwrite=TRUE'
|
||||||
|
];
|
||||||
|
$json_result = DomainZones::getLocal($admin_userdata, $data)->add();
|
||||||
|
$result = json_decode($json_result, true)['data'];
|
||||||
|
$this->assertTrue(count($result) > 1);
|
||||||
|
$foundCnt = 0;
|
||||||
|
$foundStr = '';
|
||||||
|
foreach ($result as $entry) {
|
||||||
|
if (substr($entry, 0, 7) == '_dmarc ') {
|
||||||
|
$foundCnt++;
|
||||||
|
$foundStr = $entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->assertEquals(1, $foundCnt);
|
||||||
|
$resstr = preg_replace('/\s+/', '', $foundStr);
|
||||||
|
$against = preg_replace('/\s+/', '', '_dmarc 18000 IN TXT v=DMARC1;p=none;overwrite=TRUE');
|
||||||
|
$this->assertEquals($against, $resstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @depends testCustomerDomainZonesGetWithDMARCSubdomain
|
||||||
|
*/
|
||||||
|
public function testAdminDomainZonesAddTXTCustomDMARCSubdomain()
|
||||||
|
{
|
||||||
|
global $admin_userdata;
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'domainname' => 'test2.local',
|
||||||
|
'record' => '_dmarc.mysub2',
|
||||||
|
'type' => 'TXT',
|
||||||
|
'content' => 'v=DMARC1;p=none;overwrite=TRUE'
|
||||||
|
];
|
||||||
|
$json_result = DomainZones::getLocal($admin_userdata, $data)->add();
|
||||||
|
$result = json_decode($json_result, true)['data'];
|
||||||
|
$this->assertTrue(count($result) > 1);
|
||||||
|
$foundCnt = 0;
|
||||||
|
$foundStr = '';
|
||||||
|
foreach ($result as $entry) {
|
||||||
|
if (substr($entry, 0, 14) == '_dmarc.mysub2 ') {
|
||||||
|
$foundCnt++;
|
||||||
|
$foundStr = $entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->assertEquals(1, $foundCnt);
|
||||||
|
$resstr = preg_replace('/\s+/', '', $foundStr);
|
||||||
|
$against = preg_replace('/\s+/', '', '_dmarc.mysub2 18000 IN TXT v=DMARC1;p=none;overwrite=TRUE');
|
||||||
|
$this->assertEquals($against, $resstr);
|
||||||
|
}
|
||||||
|
|
||||||
public function testAdminDomainZonesAddSRV()
|
public function testAdminDomainZonesAddSRV()
|
||||||
{
|
{
|
||||||
global $admin_userdata;
|
global $admin_userdata;
|
||||||
|
Loading…
Reference in New Issue
Block a user