refactor(dns): extend DnsValidator to DnsManager with DNS record creation
- Rename DnsValidator to DnsManager to better reflect its expanded responsibilities - Move DNS record creation logic from UnifiedEmailServer to DnsManager - Add ensureDnsRecords() method that handles both validation and creation - Consolidate internal DNS record creation (MX, SPF, DMARC) in one place - Keep DKIM key generation in UnifiedEmailServer but move DNS registration to DnsManager - Update all imports and tests to use DnsManager instead of DnsValidator - Improve code organization and discoverability of DNS functionality
This commit is contained in:
@@ -2,7 +2,7 @@ import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|||||||
import * as plugins from '../ts/plugins.js';
|
import * as plugins from '../ts/plugins.js';
|
||||||
import * as paths from '../ts/paths.js';
|
import * as paths from '../ts/paths.js';
|
||||||
import { StorageManager } from '../ts/storage/classes.storagemanager.js';
|
import { StorageManager } from '../ts/storage/classes.storagemanager.js';
|
||||||
import { DnsValidator } from '../ts/mail/routing/classes.dns.validator.js';
|
import { DnsManager } from '../ts/mail/routing/classes.dns.manager.js';
|
||||||
import { DomainRegistry } from '../ts/mail/routing/classes.domain.registry.js';
|
import { DomainRegistry } from '../ts/mail/routing/classes.domain.registry.js';
|
||||||
import { DKIMCreator } from '../ts/mail/security/classes.dkimcreator.js';
|
import { DKIMCreator } from '../ts/mail/security/classes.dkimcreator.js';
|
||||||
import type { IEmailDomainConfig } from '../ts/mail/routing/interfaces.js';
|
import type { IEmailDomainConfig } from '../ts/mail/routing/interfaces.js';
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||||
import * as plugins from '../ts/plugins.js';
|
import * as plugins from '../ts/plugins.js';
|
||||||
import * as paths from '../ts/paths.js';
|
import * as paths from '../ts/paths.js';
|
||||||
import { DnsValidator } from '../ts/mail/routing/classes.dns.validator.js';
|
import { DnsManager } from '../ts/mail/routing/classes.dns.manager.js';
|
||||||
import { DomainRegistry } from '../ts/mail/routing/classes.domain.registry.js';
|
import { DomainRegistry } from '../ts/mail/routing/classes.domain.registry.js';
|
||||||
import { StorageManager } from '../ts/storage/classes.storagemanager.js';
|
import { StorageManager } from '../ts/storage/classes.storagemanager.js';
|
||||||
import { DKIMCreator } from '../ts/mail/security/classes.dkimcreator.js';
|
import { DKIMCreator } from '../ts/mail/security/classes.dkimcreator.js';
|
||||||
@@ -21,7 +21,7 @@ class MockDcRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mock DNS resolver for testing
|
// Mock DNS resolver for testing
|
||||||
class MockDnsValidator extends DnsValidator {
|
class MockDnsManager extends DnsManager {
|
||||||
private mockNsRecords: Map<string, string[]> = new Map();
|
private mockNsRecords: Map<string, string[]> = new Map();
|
||||||
private mockTxtRecords: Map<string, string[][]> = new Map();
|
private mockTxtRecords: Map<string, string[][]> = new Map();
|
||||||
private mockMxRecords: Map<string, any[]> = new Map();
|
private mockMxRecords: Map<string, any[]> = new Map();
|
||||||
@@ -54,7 +54,7 @@ class MockDnsValidator extends DnsValidator {
|
|||||||
tap.test('DNS Validator - Forward Mode', async () => {
|
tap.test('DNS Validator - Forward Mode', async () => {
|
||||||
const testDir = plugins.path.join(paths.dataDir, '.test-dns-forward');
|
const testDir = plugins.path.join(paths.dataDir, '.test-dns-forward');
|
||||||
const mockRouter = new MockDcRouter(testDir) as any;
|
const mockRouter = new MockDcRouter(testDir) as any;
|
||||||
const validator = new DnsValidator(mockRouter);
|
const validator = new DnsManager(mockRouter);
|
||||||
|
|
||||||
const config: IEmailDomainConfig = {
|
const config: IEmailDomainConfig = {
|
||||||
domain: 'forward.example.com',
|
domain: 'forward.example.com',
|
||||||
@@ -79,7 +79,7 @@ tap.test('DNS Validator - Forward Mode', async () => {
|
|||||||
tap.test('DNS Validator - Internal DNS Mode', async () => {
|
tap.test('DNS Validator - Internal DNS Mode', async () => {
|
||||||
const testDir = plugins.path.join(paths.dataDir, '.test-dns-internal');
|
const testDir = plugins.path.join(paths.dataDir, '.test-dns-internal');
|
||||||
const mockRouter = new MockDcRouter(testDir, 'ns.myservice.com') as any;
|
const mockRouter = new MockDcRouter(testDir, 'ns.myservice.com') as any;
|
||||||
const validator = new MockDnsValidator(mockRouter);
|
const validator = new MockDnsManager(mockRouter);
|
||||||
|
|
||||||
// Setup NS delegation
|
// Setup NS delegation
|
||||||
validator.setNsRecords('mail.example.com', ['ns.myservice.com']);
|
validator.setNsRecords('mail.example.com', ['ns.myservice.com']);
|
||||||
@@ -122,7 +122,7 @@ tap.test('DNS Validator - Internal DNS Mode', async () => {
|
|||||||
tap.test('DNS Validator - External DNS Mode', async () => {
|
tap.test('DNS Validator - External DNS Mode', async () => {
|
||||||
const testDir = plugins.path.join(paths.dataDir, '.test-dns-external');
|
const testDir = plugins.path.join(paths.dataDir, '.test-dns-external');
|
||||||
const mockRouter = new MockDcRouter(testDir) as any;
|
const mockRouter = new MockDcRouter(testDir) as any;
|
||||||
const validator = new MockDnsValidator(mockRouter);
|
const validator = new MockDnsManager(mockRouter);
|
||||||
|
|
||||||
// Setup mock DNS records
|
// Setup mock DNS records
|
||||||
validator.setMxRecords('example.com', [
|
validator.setMxRecords('example.com', [
|
||||||
@@ -273,7 +273,7 @@ tap.test('DNS Record Generation', async () => {
|
|||||||
expect(dkimRecord.name).toContain('_domainkey.records.example.com');
|
expect(dkimRecord.name).toContain('_domainkey.records.example.com');
|
||||||
expect(dkimRecord.value).toContain('v=DKIM1');
|
expect(dkimRecord.value).toContain('v=DKIM1');
|
||||||
|
|
||||||
// Note: The DnsValidator doesn't have a generateDnsRecords method exposed
|
// Note: The DnsManager doesn't have a generateDnsRecords method exposed
|
||||||
// DNS records are handled internally or by the DNS server component
|
// DNS records are handled internally or by the DNS server component
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
|
@@ -26,9 +26,10 @@ interface IDnsRecords {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates DNS configuration for email domains
|
* Manages DNS configuration for email domains
|
||||||
|
* Handles both validation and creation of DNS records
|
||||||
*/
|
*/
|
||||||
export class DnsValidator {
|
export class DnsManager {
|
||||||
private dcRouter: DcRouter;
|
private dcRouter: DcRouter;
|
||||||
private storageManager: StorageManager;
|
private storageManager: StorageManager;
|
||||||
|
|
||||||
@@ -330,4 +331,197 @@ export class DnsValidator {
|
|||||||
|
|
||||||
return parts.slice(-2).join('.');
|
return parts.slice(-2).join('.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure all DNS records are created for configured domains
|
||||||
|
* This is the main entry point for DNS record management
|
||||||
|
*/
|
||||||
|
async ensureDnsRecords(domainConfigs: IEmailDomainConfig[], dkimCreator?: any): Promise<void> {
|
||||||
|
logger.log('info', `Ensuring DNS records for ${domainConfigs.length} domains`);
|
||||||
|
|
||||||
|
// First, validate all domains
|
||||||
|
const validationResults = await this.validateAllDomains(domainConfigs);
|
||||||
|
|
||||||
|
// Then create records for internal-dns domains
|
||||||
|
const internalDnsDomains = domainConfigs.filter(config => config.dnsMode === 'internal-dns');
|
||||||
|
if (internalDnsDomains.length > 0) {
|
||||||
|
await this.createInternalDnsRecords(internalDnsDomains);
|
||||||
|
|
||||||
|
// Create DKIM records if DKIMCreator is provided
|
||||||
|
if (dkimCreator) {
|
||||||
|
await this.createDkimRecords(domainConfigs, dkimCreator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log validation results for external-dns domains
|
||||||
|
for (const [domain, result] of validationResults) {
|
||||||
|
const config = domainConfigs.find(c => c.domain === domain);
|
||||||
|
if (config?.dnsMode === 'external-dns' && result.requiredChanges.length > 0) {
|
||||||
|
logger.log('warn', `External DNS configuration required for ${domain}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DNS records for internal-dns mode domains
|
||||||
|
*/
|
||||||
|
private async createInternalDnsRecords(domainConfigs: IEmailDomainConfig[]): Promise<void> {
|
||||||
|
// Check if DNS server is available
|
||||||
|
if (!this.dcRouter.dnsServer) {
|
||||||
|
logger.log('warn', 'DNS server not available, skipping internal DNS record creation');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('info', `Creating DNS records for ${domainConfigs.length} internal-dns domains`);
|
||||||
|
|
||||||
|
for (const domainConfig of domainConfigs) {
|
||||||
|
const domain = domainConfig.domain;
|
||||||
|
const ttl = domainConfig.dns?.internal?.ttl || 3600;
|
||||||
|
const mxPriority = domainConfig.dns?.internal?.mxPriority || 10;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Register MX record - points to the email domain itself
|
||||||
|
this.dcRouter.dnsServer.registerHandler(
|
||||||
|
domain,
|
||||||
|
['MX'],
|
||||||
|
() => ({
|
||||||
|
name: domain,
|
||||||
|
type: 'MX',
|
||||||
|
class: 'IN',
|
||||||
|
ttl: ttl,
|
||||||
|
data: {
|
||||||
|
priority: mxPriority,
|
||||||
|
exchange: domain
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
logger.log('info', `MX record registered for ${domain} -> ${domain} (priority ${mxPriority})`);
|
||||||
|
|
||||||
|
// Store MX record in StorageManager
|
||||||
|
await this.storageManager.set(
|
||||||
|
`/email/dns/${domain}/mx`,
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'MX',
|
||||||
|
priority: mxPriority,
|
||||||
|
exchange: domain,
|
||||||
|
ttl: ttl
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Register SPF record - allows the domain to send emails
|
||||||
|
const spfRecord = `v=spf1 a mx ~all`;
|
||||||
|
this.dcRouter.dnsServer.registerHandler(
|
||||||
|
domain,
|
||||||
|
['TXT'],
|
||||||
|
() => ({
|
||||||
|
name: domain,
|
||||||
|
type: 'TXT',
|
||||||
|
class: 'IN',
|
||||||
|
ttl: ttl,
|
||||||
|
data: spfRecord
|
||||||
|
})
|
||||||
|
);
|
||||||
|
logger.log('info', `SPF record registered for ${domain}: "${spfRecord}"`);
|
||||||
|
|
||||||
|
// Store SPF record in StorageManager
|
||||||
|
await this.storageManager.set(
|
||||||
|
`/email/dns/${domain}/spf`,
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'TXT',
|
||||||
|
data: spfRecord,
|
||||||
|
ttl: ttl
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. Register DMARC record - policy for handling email authentication
|
||||||
|
const dmarcRecord = `v=DMARC1; p=none; rua=mailto:dmarc@${domain}`;
|
||||||
|
this.dcRouter.dnsServer.registerHandler(
|
||||||
|
`_dmarc.${domain}`,
|
||||||
|
['TXT'],
|
||||||
|
() => ({
|
||||||
|
name: `_dmarc.${domain}`,
|
||||||
|
type: 'TXT',
|
||||||
|
class: 'IN',
|
||||||
|
ttl: ttl,
|
||||||
|
data: dmarcRecord
|
||||||
|
})
|
||||||
|
);
|
||||||
|
logger.log('info', `DMARC record registered for _dmarc.${domain}: "${dmarcRecord}"`);
|
||||||
|
|
||||||
|
// Store DMARC record in StorageManager
|
||||||
|
await this.storageManager.set(
|
||||||
|
`/email/dns/${domain}/dmarc`,
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'TXT',
|
||||||
|
name: `_dmarc.${domain}`,
|
||||||
|
data: dmarcRecord,
|
||||||
|
ttl: ttl
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Log summary of DNS records created
|
||||||
|
logger.log('info', `✅ DNS records created for ${domain}:
|
||||||
|
- MX: ${domain} (priority ${mxPriority})
|
||||||
|
- SPF: ${spfRecord}
|
||||||
|
- DMARC: ${dmarcRecord}
|
||||||
|
- DKIM: Will be created when keys are generated`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to create DNS records for ${domain}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DKIM DNS records for all domains
|
||||||
|
*/
|
||||||
|
private async createDkimRecords(domainConfigs: IEmailDomainConfig[], dkimCreator: any): Promise<void> {
|
||||||
|
for (const domainConfig of domainConfigs) {
|
||||||
|
const domain = domainConfig.domain;
|
||||||
|
const selector = domainConfig.dkim?.selector || 'default';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get DKIM DNS record from DKIMCreator
|
||||||
|
const dnsRecord = await dkimCreator.getDNSRecordForDomain(domain);
|
||||||
|
|
||||||
|
// For internal-dns domains, register the DNS handler
|
||||||
|
if (domainConfig.dnsMode === 'internal-dns' && this.dcRouter.dnsServer) {
|
||||||
|
const ttl = domainConfig.dns?.internal?.ttl || 3600;
|
||||||
|
|
||||||
|
this.dcRouter.dnsServer.registerHandler(
|
||||||
|
`${selector}._domainkey.${domain}`,
|
||||||
|
['TXT'],
|
||||||
|
() => ({
|
||||||
|
name: `${selector}._domainkey.${domain}`,
|
||||||
|
type: 'TXT',
|
||||||
|
class: 'IN',
|
||||||
|
ttl: ttl,
|
||||||
|
data: dnsRecord.value
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.log('info', `DKIM DNS record registered for ${selector}._domainkey.${domain}`);
|
||||||
|
|
||||||
|
// Store DKIM record in StorageManager
|
||||||
|
await this.storageManager.set(
|
||||||
|
`/email/dns/${domain}/dkim`,
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'TXT',
|
||||||
|
name: `${selector}._domainkey.${domain}`,
|
||||||
|
data: dnsRecord.value,
|
||||||
|
ttl: ttl
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For external-dns domains, just log what should be configured
|
||||||
|
if (domainConfig.dnsMode === 'external-dns') {
|
||||||
|
logger.log('info', `DKIM record for external DNS: ${dnsRecord.name} -> "${dnsRecord.value}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('warn', `Could not create DKIM DNS record for ${domain}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -19,7 +19,7 @@ import { EmailRouter } from './classes.email.router.js';
|
|||||||
import type { IEmailRoute, IEmailAction, IEmailContext, IEmailDomainConfig } from './interfaces.js';
|
import type { IEmailRoute, IEmailAction, IEmailContext, IEmailDomainConfig } from './interfaces.js';
|
||||||
import { Email } from '../core/classes.email.js';
|
import { Email } from '../core/classes.email.js';
|
||||||
import { DomainRegistry } from './classes.domain.registry.js';
|
import { DomainRegistry } from './classes.domain.registry.js';
|
||||||
import { DnsValidator } from './classes.dns.validator.js';
|
import { DnsManager } from './classes.dns.manager.js';
|
||||||
import { BounceManager, BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
|
import { BounceManager, BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
|
||||||
import { createSmtpServer } from '../delivery/smtpserver/index.js';
|
import { createSmtpServer } from '../delivery/smtpserver/index.js';
|
||||||
import { createPooledSmtpClient } from '../delivery/smtpclient/create-client.js';
|
import { createPooledSmtpClient } from '../delivery/smtpclient/create-client.js';
|
||||||
@@ -348,9 +348,10 @@ export class UnifiedEmailServer extends EventEmitter {
|
|||||||
await this.setupDkimForDomains();
|
await this.setupDkimForDomains();
|
||||||
logger.log('info', 'DKIM configuration completed for all domains');
|
logger.log('info', 'DKIM configuration completed for all domains');
|
||||||
|
|
||||||
// Set up DNS records for internal-dns mode domains
|
// Create DNS manager and ensure all DNS records are created
|
||||||
await this.setupInternalDnsRecords();
|
const dnsManager = new DnsManager(this.dcRouter);
|
||||||
logger.log('info', 'DNS records created for internal-dns domains');
|
await dnsManager.ensureDnsRecords(this.domainRegistry.getAllConfigs(), this.dkimCreator);
|
||||||
|
logger.log('info', 'DNS records ensured for all configured domains');
|
||||||
|
|
||||||
// Apply per-domain rate limits
|
// Apply per-domain rate limits
|
||||||
this.applyDomainRateLimits();
|
this.applyDomainRateLimits();
|
||||||
@@ -360,26 +361,6 @@ export class UnifiedEmailServer extends EventEmitter {
|
|||||||
await this.checkAndRotateDkimKeys();
|
await this.checkAndRotateDkimKeys();
|
||||||
logger.log('info', 'DKIM key rotation check completed');
|
logger.log('info', 'DKIM key rotation check completed');
|
||||||
|
|
||||||
// Validate DNS configuration for all domains
|
|
||||||
const dnsValidator = new DnsValidator(this.dcRouter);
|
|
||||||
const validationResults = await dnsValidator.validateAllDomains(this.domainRegistry.getAllConfigs());
|
|
||||||
|
|
||||||
// Log validation results
|
|
||||||
let hasErrors = false;
|
|
||||||
for (const [domain, result] of validationResults) {
|
|
||||||
if (!result.valid) {
|
|
||||||
hasErrors = true;
|
|
||||||
logger.log('error', `DNS validation failed for ${domain}: ${result.errors.join(', ')}`);
|
|
||||||
}
|
|
||||||
if (result.warnings.length > 0) {
|
|
||||||
logger.log('warn', `DNS warnings for ${domain}: ${result.warnings.join(', ')}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasErrors) {
|
|
||||||
logger.log('warn', 'Some domains have DNS configuration errors. Email handling may not work correctly.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip server creation in socket-handler mode
|
// Skip server creation in socket-handler mode
|
||||||
if (this.options.useSocketHandler) {
|
if (this.options.useSocketHandler) {
|
||||||
logger.log('info', 'UnifiedEmailServer started in socket-handler mode (no port listening)');
|
logger.log('info', 'UnifiedEmailServer started in socket-handler mode (no port listening)');
|
||||||
@@ -1060,160 +1041,14 @@ export class UnifiedEmailServer extends EventEmitter {
|
|||||||
// Store the private key for signing
|
// Store the private key for signing
|
||||||
this.dkimKeys.set(domain, keyPair.privateKey);
|
this.dkimKeys.set(domain, keyPair.privateKey);
|
||||||
|
|
||||||
// Extract the public key for DNS
|
// DNS record creation is now handled by DnsManager
|
||||||
const publicKeyBase64 = keyPair.publicKey
|
logger.log('info', `DKIM keys loaded for domain: ${domain} with selector: ${selector}`);
|
||||||
.replace(/-----BEGIN PUBLIC KEY-----/g, '')
|
|
||||||
.replace(/-----END PUBLIC KEY-----/g, '')
|
|
||||||
.replace(/\s/g, '');
|
|
||||||
|
|
||||||
// Register DNS handler for internal-dns mode domains
|
|
||||||
if (domainConfig.dnsMode === 'internal-dns' && this.dcRouter.dnsServer) {
|
|
||||||
const ttl = domainConfig.dns?.internal?.ttl || 3600;
|
|
||||||
|
|
||||||
this.dcRouter.dnsServer.registerHandler(
|
|
||||||
`${selector}._domainkey.${domain}`,
|
|
||||||
['TXT'],
|
|
||||||
() => ({
|
|
||||||
name: `${selector}._domainkey.${domain}`,
|
|
||||||
type: 'TXT',
|
|
||||||
class: 'IN',
|
|
||||||
ttl: ttl,
|
|
||||||
data: `v=DKIM1; k=rsa; p=${publicKeyBase64}`
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.log('info', `DKIM DNS handler registered for domain: ${domain} with selector: ${selector}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to set up DKIM for domain ${domain}: ${error.message}`);
|
logger.log('error', `Failed to set up DKIM for domain ${domain}: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up DNS records for internal-dns mode domains
|
|
||||||
* Creates MX, SPF, and DMARC records automatically
|
|
||||||
*/
|
|
||||||
private async setupInternalDnsRecords(): Promise<void> {
|
|
||||||
// Check if DNS server is available
|
|
||||||
if (!this.dcRouter.dnsServer) {
|
|
||||||
logger.log('warn', 'DNS server not available, skipping internal DNS record setup');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get domains configured for internal-dns mode
|
|
||||||
const internalDnsDomains = this.domainRegistry.getDomainsByMode('internal-dns');
|
|
||||||
|
|
||||||
if (internalDnsDomains.length === 0) {
|
|
||||||
logger.log('info', 'No domains configured for internal-dns mode');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.log('info', `Setting up DNS records for ${internalDnsDomains.length} internal-dns domains`);
|
|
||||||
|
|
||||||
for (const domainConfig of internalDnsDomains) {
|
|
||||||
const domain = domainConfig.domain;
|
|
||||||
const ttl = domainConfig.dns?.internal?.ttl || 3600;
|
|
||||||
const mxPriority = domainConfig.dns?.internal?.mxPriority || 10;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 1. Register MX record - points to the email domain itself
|
|
||||||
this.dcRouter.dnsServer.registerHandler(
|
|
||||||
domain,
|
|
||||||
['MX'],
|
|
||||||
() => ({
|
|
||||||
name: domain,
|
|
||||||
type: 'MX',
|
|
||||||
class: 'IN',
|
|
||||||
ttl: ttl,
|
|
||||||
data: {
|
|
||||||
priority: mxPriority,
|
|
||||||
exchange: domain
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
logger.log('info', `MX record registered for ${domain} -> ${domain} (priority ${mxPriority})`);
|
|
||||||
|
|
||||||
// Store MX record in StorageManager
|
|
||||||
await this.dcRouter.storageManager.set(
|
|
||||||
`/email/dns/${domain}/mx`,
|
|
||||||
JSON.stringify({
|
|
||||||
type: 'MX',
|
|
||||||
priority: mxPriority,
|
|
||||||
exchange: domain,
|
|
||||||
ttl: ttl
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// 2. Register SPF record - allows the domain to send emails
|
|
||||||
const spfRecord = `v=spf1 a mx ~all`;
|
|
||||||
this.dcRouter.dnsServer.registerHandler(
|
|
||||||
domain,
|
|
||||||
['TXT'],
|
|
||||||
() => ({
|
|
||||||
name: domain,
|
|
||||||
type: 'TXT',
|
|
||||||
class: 'IN',
|
|
||||||
ttl: ttl,
|
|
||||||
data: spfRecord
|
|
||||||
})
|
|
||||||
);
|
|
||||||
logger.log('info', `SPF record registered for ${domain}: "${spfRecord}"`);
|
|
||||||
|
|
||||||
// Store SPF record in StorageManager
|
|
||||||
await this.dcRouter.storageManager.set(
|
|
||||||
`/email/dns/${domain}/spf`,
|
|
||||||
JSON.stringify({
|
|
||||||
type: 'TXT',
|
|
||||||
data: spfRecord,
|
|
||||||
ttl: ttl
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3. Register DMARC record - policy for handling email authentication
|
|
||||||
const dmarcRecord = `v=DMARC1; p=none; rua=mailto:dmarc@${domain}`;
|
|
||||||
this.dcRouter.dnsServer.registerHandler(
|
|
||||||
`_dmarc.${domain}`,
|
|
||||||
['TXT'],
|
|
||||||
() => ({
|
|
||||||
name: `_dmarc.${domain}`,
|
|
||||||
type: 'TXT',
|
|
||||||
class: 'IN',
|
|
||||||
ttl: ttl,
|
|
||||||
data: dmarcRecord
|
|
||||||
})
|
|
||||||
);
|
|
||||||
logger.log('info', `DMARC record registered for _dmarc.${domain}: "${dmarcRecord}"`);
|
|
||||||
|
|
||||||
// Store DMARC record in StorageManager
|
|
||||||
await this.dcRouter.storageManager.set(
|
|
||||||
`/email/dns/${domain}/dmarc`,
|
|
||||||
JSON.stringify({
|
|
||||||
type: 'TXT',
|
|
||||||
name: `_dmarc.${domain}`,
|
|
||||||
data: dmarcRecord,
|
|
||||||
ttl: ttl
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// 4. Register A record - points to the server IP (if available)
|
|
||||||
// This is needed for SPF 'a' mechanism to work
|
|
||||||
// Note: We'll skip A record for now since DnsServer doesn't expose getPublicIP
|
|
||||||
// This can be added later when the server's public IP is known
|
|
||||||
logger.log('info', `A record setup skipped for ${domain} - public IP detection not available`);
|
|
||||||
|
|
||||||
// Log summary of DNS records created
|
|
||||||
logger.log('info', `✅ DNS records created for ${domain}:
|
|
||||||
- MX: ${domain} (priority ${mxPriority})
|
|
||||||
- SPF: ${spfRecord}
|
|
||||||
- DMARC: ${dmarcRecord}
|
|
||||||
- DKIM: ${domainConfig.dkim?.selector || 'default'}._domainkey.${domain}`);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
logger.log('error', `Failed to set up DNS records for ${domain}: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply per-domain rate limits from domain configurations
|
* Apply per-domain rate limits from domain configurations
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
// Email routing components
|
// Email routing components
|
||||||
export * from './classes.email.router.js';
|
export * from './classes.email.router.js';
|
||||||
export * from './classes.unified.email.server.js';
|
export * from './classes.unified.email.server.js';
|
||||||
export * from './classes.dnsmanager.js';
|
export * from './classes.dns.manager.js';
|
||||||
export * from './interfaces.js';
|
export * from './interfaces.js';
|
||||||
export * from './classes.domain.registry.js';
|
export * from './classes.domain.registry.js';
|
Reference in New Issue
Block a user