From 3ee53a0ae5c6571f18e96769763fbebf66e1282d Mon Sep 17 00:00:00 2001 From: boban Date: Fri, 17 Oct 2025 06:18:27 +0200 Subject: [PATCH] Anpassen der Tlsa Record erstellung --- app/Services/TlsaService.php | 124 +++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 19 deletions(-) diff --git a/app/Services/TlsaService.php b/app/Services/TlsaService.php index fa5645e..bd6da38 100644 --- a/app/Services/TlsaService.php +++ b/app/Services/TlsaService.php @@ -7,15 +7,58 @@ use App\Models\TlsaRecord; class TlsaService { - public function computeHashFromCert(string $host): ?string + public function resolveMxHost(): string { - $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem"; - if (!is_file($certPath)) { - // optional: Log für Debug - logger()->warning("TLSA: Zertifikat nicht gefunden", ['path' => $certPath]); + $base = env('BASE_DOMAIN', 'example.com'); + $sub = env('MTA_SUB', 'mx') ?: 'mx'; + return "{$sub}.{$base}"; + } + + private function certCandidates(string $host): array + { + // bevorzugt die vom Deploy-Hook gesetzten, lesbaren Symlinks + $candidates = [ + '/etc/ssl/mail/fullchain.pem', // MX + "/etc/letsencrypt/live/{$host}/fullchain.pem", // Fallback + ]; + return $candidates; + } + + public function refreshForMx(string $service = '_25._tcp'): ?\App\Models\TlsaRecord + { + $host = $this->resolveMxHost(); + + // bevorzugt die als Server markierte Domain + $serverDomain = \App\Models\Domain::where('is_server', true)->first(); + + // Fallback: Domain über FQDN (mx.) finden + if (!$serverDomain) { + $serverDomain = \App\Models\Domain::where('domain', $host)->first(); + } + + if (!$serverDomain) { + // Keine passende Domain in der DB → nichts tun return null; } + return $this->refreshForServerDomain($serverDomain, $service); + } + + private function firstReadableCert(string $host): ?string + { + foreach ($this->certCandidates($host) as $path) { + if (is_file($path) && is_readable($path)) { + return $path; + } + } + return null; + } + + public function computeHashFromCert(string $host): ?string + { + $certPath = $this->firstReadableCert($host); + if (!$certPath) return null; + $cmd = "openssl x509 -in ".escapeshellarg($certPath)." -noout -pubkey" . " | openssl pkey -pubin -outform DER" . " | openssl dgst -sha256"; @@ -24,38 +67,81 @@ class TlsaService return $hash !== '' ? $hash : null; } - /** - * TLSA (3 1 1) für den MX-Host der übergebenen Domain erzeugen/aktualisieren. - * Host kommt DIREKT aus $serverDomain->domain, nicht aus ENV. - */ public function refreshForServerDomain(Domain $serverDomain, string $service = '_25._tcp'): ?TlsaRecord { - $host = $serverDomain->domain; // <— wichtig: Domain-Objekt statt ENV + $host = $this->resolveMxHost(); $hash = $this->computeHashFromCert($host); - if (!$hash) { - return null; // Zert (noch) nicht vorhanden - } + if (!$hash) return null; - $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem"; + $certPath = $this->firstReadableCert($host) ?? ''; $rec = TlsaRecord::updateOrCreate( ['domain_id' => $serverDomain->id, 'host' => $host, 'service' => $service], [ - 'usage' => 3, // DANE-EE - 'selector' => 1, // SPKI - 'matching' => 1, // SHA-256 + 'usage' => 3, + 'selector' => 1, + 'matching' => 1, 'hash' => $hash, 'cert_path' => $certPath, ] ); - // optional: Datei für Export/Debug @mkdir('/etc/mailwolt/dns', 0755, true); - @file_put_contents("/etc/mailwolt/dns/{$host}.tlsa.txt", sprintf('%s.%s IN TLSA 3 1 1 %s', $service, $host, $hash)."\n"); + $line = sprintf('%s.%s IN TLSA %d %d %d %s', $service, $host, 3, 1, 1, $hash); + @file_put_contents("/etc/mailwolt/dns/{$host}.tlsa.txt", $line."\n"); return $rec; } +// public function computeHashFromCert(string $host): ?string +// { +// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem"; +// if (!is_file($certPath)) { +// // optional: Log für Debug +// logger()->warning("TLSA: Zertifikat nicht gefunden", ['path' => $certPath]); +// return null; +// } +// +// $cmd = "openssl x509 -in ".escapeshellarg($certPath)." -noout -pubkey" +// . " | openssl pkey -pubin -outform DER" +// . " | openssl dgst -sha256"; +// $out = shell_exec($cmd.' 2>/dev/null') ?? ''; +// $hash = preg_replace('/^SHA256\(stdin\)=\s*/', '', trim($out)); +// return $hash !== '' ? $hash : null; +// } +// +// /** +// * TLSA (3 1 1) für den MX-Host der übergebenen Domain erzeugen/aktualisieren. +// * Host kommt DIREKT aus $serverDomain->domain, nicht aus ENV. +// */ +// public function refreshForServerDomain(Domain $serverDomain, string $service = '_25._tcp'): ?TlsaRecord +// { +// $host = $serverDomain->domain; // <— wichtig: Domain-Objekt statt ENV +// $hash = $this->computeHashFromCert($host); +// if (!$hash) { +// return null; // Zert (noch) nicht vorhanden +// } +// +// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem"; +// +// $rec = TlsaRecord::updateOrCreate( +// ['domain_id' => $serverDomain->id, 'host' => $host, 'service' => $service], +// [ +// 'usage' => 3, // DANE-EE +// 'selector' => 1, // SPKI +// 'matching' => 1, // SHA-256 +// 'hash' => $hash, +// 'cert_path' => $certPath, +// ] +// ); +// +// // optional: Datei für Export/Debug +// @mkdir('/etc/mailwolt/dns', 0755, true); +// @file_put_contents("/etc/mailwolt/dns/{$host}.tlsa.txt", sprintf('%s.%s IN TLSA 3 1 1 %s', $service, $host, $hash)."\n"); +// +// return $rec; +// } + // public function resolveMxHost(): string // { // $base = env('BASE_DOMAIN', 'example.com');