From c2dab0a56baa004bc3ccd72554e96e3838baa807 Mon Sep 17 00:00:00 2001 From: boban Date: Fri, 17 Oct 2025 03:00:20 +0200 Subject: [PATCH] Anpassen der Tlsa Record erstellung --- database/seeders/SystemDomainSeeder.php | 173 ++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 8 deletions(-) diff --git a/database/seeders/SystemDomainSeeder.php b/database/seeders/SystemDomainSeeder.php index 89fd8d6..3fe03b8 100644 --- a/database/seeders/SystemDomainSeeder.php +++ b/database/seeders/SystemDomainSeeder.php @@ -6,6 +6,9 @@ namespace Database\Seeders; use App\Models\DkimKey; use App\Models\Domain; use App\Models\MailUser; +use App\Models\TlsaRecord; + +// ← NEU: fürs exists() use App\Services\DnsRecordService; use App\Services\TlsaService; use Illuminate\Database\Seeder; @@ -21,11 +24,11 @@ class SystemDomainSeeder extends Seeder return; } - $mtaSub = env('MTA_SUB', 'mx') ?: 'mx'; // z.B. mx - $serverFqdn = "{$mtaSub}.{$platformBase}"; // z.B. mx.nexlab.at + $mtaSub = env('MTA_SUB', 'mx') ?: 'mx'; // z.B. mx + $serverFqdn = "{$mtaSub}.{$platformBase}"; // z.B. mx.nexlab.at - $systemSub = config('mailpool.platform_system_zone') ?: 'sysmail'; // z.B. sysmail - $systemFqdn = "{$systemSub}.{$platformBase}"; // z.B. sysmail.nexlab.at + $systemSub = config('mailpool.platform_system_zone') ?: 'sysmail'; + $systemFqdn = "{$systemSub}.{$platformBase}"; // z.B. sysmail.nexlab.at // ========================================================================= // 1) MAILSERVER-DOMAIN zuerst (mx.), is_server = true @@ -41,9 +44,14 @@ class SystemDomainSeeder extends Seeder $this->command->info("Server-Host markiert: {$serverDomain->domain}"); } - // TLSA (3 1 1) nur in Laravel erzeugen/aktualisieren (falls LE-Zert bereits da ist) - if (! $serverDomain->tlsa()->exists()) { - $tlsa = app(\App\Services\TlsaService::class)->refreshForServerDomain($serverDomain); + // --- TLSA (3 1 1) prüfen/erzeugen (nur wenn LE-Zert schon da ist) ----- + $hasTlsa = TlsaRecord::where('domain_id', $serverDomain->id) + ->where('host', $serverFqdn) + ->where('service', '_25._tcp') + ->exists(); + + if (!$hasTlsa) { + $tlsa = app(TlsaService::class)->refreshForServerDomain($serverDomain); if ($tlsa) { $this->command->info("TLSA erstellt: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}"); } else { @@ -119,7 +127,6 @@ class SystemDomainSeeder extends Seeder openssl_pkey_export($res, $privateKeyPem); $details = openssl_pkey_get_details($res); $pubDer = $details['key']; - // Public PEM zu "p=" (reines Base64) normalisieren $publicTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer)); return [$privateKeyPem, $publicTxt]; } @@ -148,6 +155,156 @@ class SystemDomainSeeder extends Seeder } } + +//namespace Database\Seeders; +// +//use App\Models\DkimKey; +//use App\Models\Domain; +//use App\Models\MailUser; +//use App\Services\DnsRecordService; +//use App\Services\TlsaService; +//use Illuminate\Database\Seeder; +// +//class SystemDomainSeeder extends Seeder +//{ +// public function run(): void +// { +// // --- Basiswerte aus Config/ENV --- +// $platformBase = config('mailpool.platform_zone', env('BASE_DOMAIN', 'example.com')); // z.B. nexlab.at +// if (!$platformBase || $platformBase === 'example.com') { +// $this->command->warn("BASE_DOMAIN ist 'example.com' – Seeder überspringt produktive Einträge."); +// return; +// } +// +// $mtaSub = env('MTA_SUB', 'mx') ?: 'mx'; // z.B. mx +// $serverFqdn = "{$mtaSub}.{$platformBase}"; // z.B. mx.nexlab.at +// +// $systemSub = config('mailpool.platform_system_zone') ?: 'sysmail'; // z.B. sysmail +// $systemFqdn = "{$systemSub}.{$platformBase}"; // z.B. sysmail.nexlab.at +// +// // ========================================================================= +// // 1) MAILSERVER-DOMAIN zuerst (mx.), is_server = true +// // ========================================================================= +// $serverDomain = Domain::firstOrCreate( +// ['domain' => $serverFqdn], +// ['is_active' => true, 'is_system' => false, 'is_server' => true] +// ); +// +// if (!$serverDomain->is_server) { +// $serverDomain->is_server = true; +// $serverDomain->save(); +// $this->command->info("Server-Host markiert: {$serverDomain->domain}"); +// } +// +// // TLSA (3 1 1) nur in Laravel erzeugen/aktualisieren (falls LE-Zert bereits da ist) +// if (! $serverDomain->tlsa()->exists()) { +// $tlsa = app(\App\Services\TlsaService::class)->refreshForServerDomain($serverDomain); +// if ($tlsa) { +// $this->command->info("TLSA erstellt: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}"); +// } else { +// $this->command->warn("TLSA übersprungen: LE-Zertifikat (noch) nicht vorhanden – später erneut seeden."); +// } +// } else { +// $this->command->line("TLSA bereits vorhanden, übersprungen."); +// } +// +// // ========================================================================= +// // 2) SYSTEM-DOMAIN danach (sysmail.), is_system = true +// // ========================================================================= +// $systemDomain = Domain::firstOrCreate( +// ['domain' => $systemFqdn], +// ['is_active' => true, 'is_system' => true] +// ); +// +// // System-Absender (no-reply) – ohne Passwort (kein Login) +// MailUser::firstOrCreate( +// ['email' => "no-reply@{$systemFqdn}"], +// [ +// 'domain_id' => $systemDomain->id, +// 'localpart' => 'no-reply', +// 'password_hash' => null, +// 'is_active' => true, +// 'is_system' => true, +// 'must_change_pw' => false, +// 'quota_mb' => 0, +// ] +// ); +// +// // DKIM – Key erzeugen, falls keiner aktiv existiert +// if (!$systemDomain->dkimKeys()->where('is_active', true)->exists()) { +// [$privPem, $pubTxt] = $this->generateDkimKeyPair(); +// $selector = 'mwl1'; // frei wählbar, später rotieren +// +// DkimKey::create([ +// 'domain_id' => $systemDomain->id, +// 'selector' => $selector, +// 'private_key_pem' => $privPem, +// 'public_key_txt' => $pubTxt, +// 'is_active' => true, +// ]); +// +// $this->command->info("DKIM angelegt: Host = {$selector}._domainkey.{$systemFqdn}"); +// } +// +// $dk = $systemDomain->dkimKeys()->where('is_active', true)->latest()->first(); +// $dkimTxt = $dk ? "v=DKIM1; k=rsa; p={$dk->public_key_txt}" : null; +// +// app(DnsRecordService::class)->provision( +// $systemDomain, +// dkimSelector: $dk?->selector, +// dkimTxt: $dkimTxt, +// opts: [ +// 'dmarc_policy' => 'none', +// 'spf_tail' => '-all', +// // optional: 'ipv4' => $serverIp, 'ipv6' => ... +// ] +// ); +// +// $this->command->info("System-Domain '{$systemFqdn}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen."); +// $this->printDnsHints($systemDomain); +// } +// +// /** @return array{0:string privatePem,1:string publicTxt} */ +// private function generateDkimKeyPair(): array +// { +// $res = openssl_pkey_new([ +// 'private_key_bits' => 2048, +// 'private_key_type' => OPENSSL_KEYTYPE_RSA, +// ]); +// openssl_pkey_export($res, $privateKeyPem); +// $details = openssl_pkey_get_details($res); +// $pubDer = $details['key']; +// // Public PEM zu "p=" (reines Base64) normalisieren +// $publicTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer)); +// return [$privateKeyPem, $publicTxt]; +// } +// +// private function printDnsHints(Domain $domain): void +// { +// $base = $domain->domain; +// +// $dkim = $domain->dkimKeys()->where('is_active', true)->latest()->first(); +// if ($dkim) { +// $this->command->line(" • DKIM TXT @ {$dkim->selector}._domainkey.{$base}"); +// $this->command->line(" v=DKIM1; k=rsa; p={$dkim->public_key_txt}"); +// } +// +// $spf = $domain->spf()->where('is_active', true)->latest()->first(); +// if ($spf) { +// $this->command->line(" • SPF TXT @ {$base}"); +// $this->command->line(" {$spf->record_txt}"); +// } +// +// $dmarc = $domain->dmarc()->where('is_active', true)->latest()->first(); +// if ($dmarc) { +// $this->command->line(" • DMARC TXT @ _dmarc.{$base}"); +// $this->command->line(" " . ($dmarc->record_txt ?? $dmarc->renderTxt())); +// } +// } +//} +// +//---------------------------------------------------------------- + // //namespace Database\Seeders; //