diff --git a/app/Console/Commands/TlsaRefresh.php b/app/Console/Commands/TlsaRefresh.php index b805bfe..3a6f646 100644 --- a/app/Console/Commands/TlsaRefresh.php +++ b/app/Console/Commands/TlsaRefresh.php @@ -21,9 +21,9 @@ class TlsaRefresh extends Command return self::SUCCESS; } - $rec = $tlsa->refreshForMx(); + $rec = $tlsa->refreshForMx(); // <─ HIER if (!$rec) { - $this->warn('TLSA konnte nicht aktualisiert werden (Zertifikat fehlt?).'); + $this->warn('TLSA konnte nicht aktualisiert werden (is_server / Zertifikat / Rechte?).'); return self::FAILURE; } $this->info("TLSA ok: {$rec->service}.{$rec->host} 3 1 1 {$rec->hash}"); diff --git a/app/Services/TlsaService.php b/app/Services/TlsaService.php index bd6da38..e196c74 100644 --- a/app/Services/TlsaService.php +++ b/app/Services/TlsaService.php @@ -1,5 +1,4 @@ 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; - } + foreach ($this->certCandidates($host) as $p) { + if (is_file($p) && is_readable($p)) return $p; } return null; } - public function computeHashFromCert(string $host): ?string + public function computeHashFromCertPath(string $certPath): ?string { - $certPath = $this->firstReadableCert($host); - if (!$certPath) return null; - - $cmd = "openssl x509 -in ".escapeshellarg($certPath)." -noout -pubkey" + $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)); + $out = shell_exec($cmd . ' 2>/dev/null') ?? ''; + $out = trim($out); + if ($out === '') return null; + + // Variationen entfernen: "SHA256(stdin)= x", "SHA2-256(stdin)= x" + $hash = preg_replace('/^SHA2?56\(stdin\)=\s*/i', '', $out); + $hash = preg_replace('/^SHA2?-256\(stdin\)=\s*/i', '', $hash); // falls "SHA2-256" + $hash = trim($hash); + return $hash !== '' ? $hash : null; } - public function refreshForServerDomain(Domain $serverDomain, string $service = '_25._tcp'): ?TlsaRecord + /** Holt is_server-Domain und schreibt/aktualisiert TLSA (3 1 1) */ + public function refreshForMx(string $service = '_25._tcp'): ?TlsaRecord { $host = $this->resolveMxHost(); - $hash = $this->computeHashFromCert($host); + + // 1) Server-Domain ermitteln (es darf genau eine mit is_server=true geben) + $serverDomain = Domain::where('is_server', true) + ->where('domain', $host) // doppelt absichern: passt zur ENV + ->first(); + + if (!$serverDomain) { + // Kein is_server Eintrag -> nichts zu tun + return null; + } + + // 2) Zertifikat finden/lesen + $certPath = $this->firstReadableCert($host); + if (!$certPath) return null; + + // 3) Hash berechnen + $hash = $this->computeHashFromCertPath($certPath); if (!$hash) return null; - $certPath = $this->firstReadableCert($host) ?? ''; - + // 4) DB schreiben (idempotent) $rec = TlsaRecord::updateOrCreate( ['domain_id' => $serverDomain->id, 'host' => $host, 'service' => $service], [ - 'usage' => 3, - 'selector' => 1, - 'matching' => 1, - 'hash' => $hash, + 'usage' => 3, + 'selector' => 1, + 'matching' => 1, + 'hash' => $hash, 'cert_path' => $certPath, ] ); + // 5) optionale Export-Datei @mkdir('/etc/mailwolt/dns', 0755, true); $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"); + @file_put_contents("/etc/mailwolt/dns/{$host}.tlsa.txt", $line . "\n"); return $rec; } +} + + + +//namespace App\Services; +// +//use App\Models\Domain; +//use App\Models\TlsaRecord; +// +//class TlsaService +//{ +// public function resolveMxHost(): string +// { +// $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"; +// $out = shell_exec($cmd.' 2>/dev/null') ?? ''; +// $hash = preg_replace('/^SHA256\(stdin\)=\s*/', '', trim($out)); +// return $hash !== '' ? $hash : null; +// } +// +// public function refreshForServerDomain(Domain $serverDomain, string $service = '_25._tcp'): ?TlsaRecord +// { +// $host = $this->resolveMxHost(); +// $hash = $this->computeHashFromCert($host); +// if (!$hash) return null; +// +// $certPath = $this->firstReadableCert($host) ?? ''; +// +// $rec = TlsaRecord::updateOrCreate( +// ['domain_id' => $serverDomain->id, 'host' => $host, 'service' => $service], +// [ +// 'usage' => 3, +// 'selector' => 1, +// 'matching' => 1, +// 'hash' => $hash, +// 'cert_path' => $certPath, +// ] +// ); +// +// @mkdir('/etc/mailwolt/dns', 0755, true); +// $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 // { @@ -194,7 +286,7 @@ class TlsaService // // return $rec; // } -} +//} //class TlsaService