Domain Create Modal anpassen Fehler auf Null

main
boban 2025-10-17 19:04:47 +02:00
parent d99913df06
commit dc9a5fdd48
1 changed files with 395 additions and 280 deletions

View File

@ -1,4 +1,5 @@
<?php <?php
namespace App\Services; namespace App\Services;
use App\Models\Domain; use App\Models\Domain;
@ -16,8 +17,8 @@ class TlsaService
private function certCandidates(string $host): array private function certCandidates(string $host): array
{ {
return [ return [
'/etc/ssl/mail/fullchain.pem', // vom Deploy-Wrapper kopiert (lesbar) '/etc/ssl/mail/fullchain.pem',
"/etc/letsencrypt/live/{$host}/fullchain.pem", // Fallback (meist nicht lesbar) "/etc/letsencrypt/live/{$host}/fullchain.pem",
]; ];
} }
@ -34,42 +35,33 @@ class TlsaService
$cmd = "openssl x509 -in " . escapeshellarg($certPath) . " -noout -pubkey" $cmd = "openssl x509 -in " . escapeshellarg($certPath) . " -noout -pubkey"
. " | openssl pkey -pubin -outform DER" . " | openssl pkey -pubin -outform DER"
. " | openssl dgst -sha256"; . " | openssl dgst -sha256";
$out = shell_exec($cmd . ' 2>/dev/null') ?? ''; $out = trim(shell_exec($cmd . ' 2>/dev/null') ?? '');
$out = trim($out);
if ($out === '') return null; if ($out === '') return null;
// Variationen entfernen: "SHA256(stdin)= x", "SHA2-256(stdin)= x" // Akzeptiere "SHA256(stdin)= x" und "SHA2-256(stdin)= x"
$hash = preg_replace('/^SHA2?56\(stdin\)=\s*/i', '', $out); $hash = preg_replace('/^SHA2?-?256\(stdin\)=\s*/i', '', $out);
$hash = preg_replace('/^SHA2?-256\(stdin\)=\s*/i', '', $hash); // falls "SHA2-256"
$hash = trim($hash); $hash = trim($hash);
return $hash !== '' ? $hash : null; return $hash !== '' ? strtolower($hash) : null;
} }
/** Holt is_server-Domain und schreibt/aktualisiert TLSA (3 1 1) */ /** Erzeugt/aktualisiert TLSA (3 1 1) für den aktuellen MX-Host (is_server-Domain). */
public function refreshForMx(string $service = '_25._tcp'): ?TlsaRecord public function refreshForMx(string $service = '_25._tcp'): ?TlsaRecord
{ {
$host = $this->resolveMxHost(); $host = $this->resolveMxHost();
// 1) Server-Domain ermitteln (es darf genau eine mit is_server=true geben)
$serverDomain = Domain::where('is_server', true) $serverDomain = Domain::where('is_server', true)
->where('domain', $host) // doppelt absichern: passt zur ENV ->where('domain', $host) // Absicherung
->first(); ->first();
if (!$serverDomain) { if (!$serverDomain) return null;
// Kein is_server Eintrag -> nichts zu tun
return null;
}
// 2) Zertifikat finden/lesen
$certPath = $this->firstReadableCert($host); $certPath = $this->firstReadableCert($host);
if (!$certPath) return null; if (!$certPath) return null;
// 3) Hash berechnen
$hash = $this->computeHashFromCertPath($certPath); $hash = $this->computeHashFromCertPath($certPath);
if (!$hash) return null; if (!$hash) return null;
// 4) DB schreiben (idempotent)
$rec = TlsaRecord::updateOrCreate( $rec = TlsaRecord::updateOrCreate(
['domain_id' => $serverDomain->id, 'host' => $host, 'service' => $service], ['domain_id' => $serverDomain->id, 'host' => $host, 'service' => $service],
[ [
@ -81,17 +73,20 @@ class TlsaService
] ]
); );
// 5) optionale Export-Datei
@mkdir('/etc/mailwolt/dns', 0755, true); @mkdir('/etc/mailwolt/dns', 0755, true);
$line = sprintf('%s.%s IN TLSA %d %d %d %s', $service, $host, 3, 1, 1, $hash); $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; return $rec;
} }
/** 🔧 Alias für Seeder-Kompatibilität */
public function prefreshForServerDomain(?Domain $unused = null): ?TlsaRecord
{
return $this->refreshForMx('_25._tcp');
}
} }
//namespace App\Services; //namespace App\Services;
// //
//use App\Models\Domain; //use App\Models\Domain;
@ -99,316 +94,436 @@ class TlsaService
// //
//class TlsaService //class TlsaService
//{ //{
// public function refreshForServerDomain(Domain $serverDomain): ?TlsaRecord
// {
// // Falls du bereits eine Methode hast, die den Server-TLSA erzeugt:
// if (method_exists($this, 'refreshForServer')) {
// return $this->refreshForServer();
// }
//
// // Minimal-Implementierung (TLSA 3 1 1 aus aktuellem Mail-Zertifikat)
// $host = env('MTA_HOST', $serverDomain->mx_host ?? ("mx.".$serverDomain->domain));
// $service = '_25._tcp';
// $cert = env('MAIL_CERT', "/etc/ssl/mail/fullchain.pem");
// $pubkeyDer = trim(shell_exec(
// "openssl x509 -in ".escapeshellarg($cert)." -pubkey -noout | ".
// "openssl pkey -pubin -outform DER 2>/dev/null | ".
// "openssl dgst -sha256 -r | awk '{print \$1}'"
// ) ?? '');
//
// if ($pubkeyDer === '') {
// return null;
// }
//
// return TlsaRecord::updateOrCreate(
// ['service' => $service, 'host' => $host],
// ['usage' => 3, 'selector' => 1, 'matching' => 1, 'hash' => strtolower($pubkeyDer)]
// );
// }
//
// public function resolveMxHost(): string // public function resolveMxHost(): string
// { // {
// $base = env('BASE_DOMAIN', 'example.com'); // $base = env('BASE_DOMAIN', 'example.com');
// $sub = env('MTA_SUB', 'mx') ?: 'mx'; // $sub = env('MTA_SUB', 'mx') ?: 'mx';
// return "{$sub}.{$base}"; // return "{$sub}.{$base}";
// } // }
// //
// private function certCandidates(string $host): array // private function certCandidates(string $host): array
// { // {
// // bevorzugt die vom Deploy-Hook gesetzten, lesbaren Symlinks // return [
// $candidates = [ // '/etc/ssl/mail/fullchain.pem', // vom Deploy-Wrapper kopiert (lesbar)
// '/etc/ssl/mail/fullchain.pem', // MX // "/etc/letsencrypt/live/{$host}/fullchain.pem", // Fallback (meist nicht lesbar)
// "/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.<BASE_DOMAIN>) 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 // private function firstReadableCert(string $host): ?string
// { // {
// foreach ($this->certCandidates($host) as $path) { // foreach ($this->certCandidates($host) as $p) {
// if (is_file($path) && is_readable($path)) { // if (is_file($p) && is_readable($p)) return $p;
// return $path;
// }
// } // }
// return null; // return null;
// } // }
// //
// public function computeHashFromCert(string $host): ?string // public function computeHashFromCertPath(string $certPath): ?string
// { // {
// $certPath = $this->firstReadableCert($host); // $cmd = "openssl x509 -in " . escapeshellarg($certPath) . " -noout -pubkey"
// if (!$certPath) return null;
//
// $cmd = "openssl x509 -in ".escapeshellarg($certPath)." -noout -pubkey"
// . " | openssl pkey -pubin -outform DER" // . " | openssl pkey -pubin -outform DER"
// . " | openssl dgst -sha256"; // . " | openssl dgst -sha256";
// $out = shell_exec($cmd.' 2>/dev/null') ?? ''; // $out = shell_exec($cmd . ' 2>/dev/null') ?? '';
// $hash = preg_replace('/^SHA256\(stdin\)=\s*/', '', trim($out)); // $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; // 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(); // $host = $this->resolveMxHost();
// $hash = $this->computeHashFromCert($host);
// if (!$hash) return null;
// //
// $certPath = $this->firstReadableCert($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();
// //
// $rec = TlsaRecord::updateOrCreate( // if (!$serverDomain) {
// ['domain_id' => $serverDomain->id, 'host' => $host, 'service' => $service], // // Kein is_server Eintrag -> nichts zu tun
// [
// '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
// {
// $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; // return null;
// } // }
// //
// $cmd = "openssl x509 -in ".escapeshellarg($certPath)." -noout -pubkey" // // 2) Zertifikat finden/lesen
// . " | openssl pkey -pubin -outform DER" // $certPath = $this->firstReadableCert($host);
// . " | openssl dgst -sha256"; // if (!$certPath) return null;
// $out = shell_exec($cmd.' 2>/dev/null') ?? '';
// $hash = preg_replace('/^SHA256\(stdin\)=\s*/', '', trim($out));
// return $hash !== '' ? $hash : null;
// }
// //
// /** // // 3) Hash berechnen
// * TLSA (3 1 1) für den MX-Host der übergebenen Domain erzeugen/aktualisieren. // $hash = $this->computeHashFromCertPath($certPath);
// * Host kommt DIREKT aus $serverDomain->domain, nicht aus ENV. // if (!$hash) return null;
// */
// 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";
// //
// // 4) DB schreiben (idempotent)
// $rec = TlsaRecord::updateOrCreate( // $rec = TlsaRecord::updateOrCreate(
// ['domain_id' => $serverDomain->id, 'host' => $host, 'service' => $service], // ['domain_id' => $serverDomain->id, 'host' => $host, 'service' => $service],
// [ // [
// 'usage' => 3, // DANE-EE // 'usage' => 3,
// 'selector' => 1, // SPKI // 'selector' => 1,
// 'matching' => 1, // SHA-256 // 'matching' => 1,
// 'hash' => $hash, // 'hash' => $hash,
// 'cert_path' => $certPath, // 'cert_path' => $certPath,
// ] // ]
// ); // );
// //
// // optional: Datei für Export/Debug // // 5) optionale Export-Datei
// @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');
// $sub = env('MTA_SUB', 'mx') ?: 'mx';
// return "{$sub}.{$base}";
// }
//
// public function computeHashFromCert(string $host): ?string
// {
// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
// if (!is_file($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;
// }
//
// /**
// * Schreibt/aktualisiert TLSA (3 1 1) für den MX-Host in DB
// * und legt zusätzlich /etc/mailwolt/dns/<host>.tlsa.txt ab.
// */
// public function refreshForServerDomain(Domain $serverDomain, string $service = '_25._tcp'): ?TlsaRecord
// {
// $host = $this->resolveMxHost();
// $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: TXT-Datei für Export/Debug
// @mkdir('/etc/mailwolt/dns', 0755, true); // @mkdir('/etc/mailwolt/dns', 0755, true);
// $line = sprintf('%s.%s IN TLSA %d %d %d %s', $service, $host, 3, 1, 1, $hash); // $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;
// }
//}
//class TlsaService
//{
// public function resolveMtaHost(): string
// {
// $base = env('BASE_DOMAIN', 'example.com');
// $sub = env('MTA_SUB', 'mx') ?: 'mx';
// return "{$sub}.{$base}";
// }
//
// public function computeHashFromCert(string $host): ?string
// {
// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
// if (!is_file($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 refreshForMx(string $service = '_25._tcp'): ?TlsaRecord
// {
// $host = $this->resolveMtaHost();
// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
// $hash = $this->computeHashFromCert($host);
// if (!$hash) return null;
//
// // DB upsert (global, domain_id = null)
// $rec = TlsaRecord::updateOrCreate(
// ['domain_id' => null, 'host' => $host, 'service' => $service],
// [
// 'usage' => 3, // DANE-EE
// 'selector' => 1, // SPKI
// 'matching' => 1, // SHA-256
// 'hash' => $hash,
// 'cert_path' => $certPath,
// ]
// );
//
// // Datei nur aktualisieren, wenn sich der Hash ändert
// @mkdir('/etc/mailwolt/dns', 0755, true);
// $file = "/etc/mailwolt/dns/{$host}.tlsa.txt";
// $newLine = sprintf('%s.%s IN TLSA %d %d %d %s', $service, $host, 3, 1, 1, $hash);
//
// $needWrite = true;
// if (is_file($file)) {
// $current = trim((string)file_get_contents($file));
// if ($current === $newLine) {
// $needWrite = false;
// }
// }
// if ($needWrite) {
// file_put_contents($file, $newLine."\n");
// }
// //
// return $rec; // return $rec;
// } // }
//} //}
// //
//----
// //
//namespace App\Services;
// //
//use App\Models\TlsaRecord; ////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.<BASE_DOMAIN>) 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;
//// }
// //
//class TlsaService //// public function computeHashFromCert(string $host): ?string
//{ //// {
// public function resolveMtaHost(): string //// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
// { //// if (!is_file($certPath)) {
// $base = env('BASE_DOMAIN', 'example.com'); //// // optional: Log für Debug
// $sub = env('MTA_SUB', 'mx') ?: 'mx'; //// logger()->warning("TLSA: Zertifikat nicht gefunden", ['path' => $certPath]);
// return "{$sub}.{$base}"; //// 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 computeHashFromCert(string $host): ?string //// public function resolveMxHost(): string
// { //// {
// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem"; //// $base = env('BASE_DOMAIN', 'example.com');
// if (!is_file($certPath)) return null; //// $sub = env('MTA_SUB', 'mx') ?: 'mx';
//// return "{$sub}.{$base}";
//// }
////
//// public function computeHashFromCert(string $host): ?string
//// {
//// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
//// if (!is_file($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;
//// }
////
//// /**
//// * Schreibt/aktualisiert TLSA (3 1 1) für den MX-Host in DB
//// * und legt zusätzlich /etc/mailwolt/dns/<host>.tlsa.txt ab.
//// */
//// public function refreshForServerDomain(Domain $serverDomain, string $service = '_25._tcp'): ?TlsaRecord
//// {
//// $host = $this->resolveMxHost();
//// $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: TXT-Datei für Export/Debug
//// @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;
//// }
////}
// //
// $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;
// }
// //
// /** ////class TlsaService
// * Schreibt/aktualisiert TLSA in DB (global) + Datei unter /etc/mailwolt/dns/. ////{
// * Wir speichern ohne domain_id (global, nur pro Host/Service). //// public function resolveMtaHost(): string
// */ //// {
// public function refreshForMx(string $service = '_25._tcp'): ?TlsaRecord //// $base = env('BASE_DOMAIN', 'example.com');
// { //// $sub = env('MTA_SUB', 'mx') ?: 'mx';
// $host = $this->resolveMtaHost(); //// return "{$sub}.{$base}";
// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem"; //// }
// $hash = $this->computeHashFromCert($host); ////
// if (!$hash) return null; //// public function computeHashFromCert(string $host): ?string
//// {
//// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
//// if (!is_file($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 refreshForMx(string $service = '_25._tcp'): ?TlsaRecord
//// {
//// $host = $this->resolveMtaHost();
//// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
//// $hash = $this->computeHashFromCert($host);
//// if (!$hash) return null;
////
//// // DB upsert (global, domain_id = null)
//// $rec = TlsaRecord::updateOrCreate(
//// ['domain_id' => null, 'host' => $host, 'service' => $service],
//// [
//// 'usage' => 3, // DANE-EE
//// 'selector' => 1, // SPKI
//// 'matching' => 1, // SHA-256
//// 'hash' => $hash,
//// 'cert_path' => $certPath,
//// ]
//// );
////
//// // Datei nur aktualisieren, wenn sich der Hash ändert
//// @mkdir('/etc/mailwolt/dns', 0755, true);
//// $file = "/etc/mailwolt/dns/{$host}.tlsa.txt";
//// $newLine = sprintf('%s.%s IN TLSA %d %d %d %s', $service, $host, 3, 1, 1, $hash);
////
//// $needWrite = true;
//// if (is_file($file)) {
//// $current = trim((string)file_get_contents($file));
//// if ($current === $newLine) {
//// $needWrite = false;
//// }
//// }
//// if ($needWrite) {
//// file_put_contents($file, $newLine."\n");
//// }
////
//// return $rec;
//// }
////}
////
////----
// //
// // DB upsert (domain_id = null → globaler Eintrag)
// $rec = TlsaRecord::updateOrCreate(
// ['domain_id' => null, 'host' => $host, 'service' => $service],
// [
// 'usage' => 3, // DANE-EE
// 'selector' => 1, // SPKI
// 'matching' => 1, // SHA-256
// 'hash' => $hash,
// 'cert_path' => $certPath,
// ]
// );
// //
// // Datei schreiben (für externen DNS-Export etc.) ////
// @mkdir('/etc/mailwolt/dns', 0755, true); ////namespace App\Services;
// $line = sprintf('%s.%s IN TLSA %d %d %d %s', ////
// $service, $host, 3, 1, 1, $hash); ////use App\Models\TlsaRecord;
// file_put_contents("/etc/mailwolt/dns/{$host}.tlsa.txt", $line."\n"); ////
// ////class TlsaService
// return $rec; ////{
// } //// public function resolveMtaHost(): string
//} //// {
//// $base = env('BASE_DOMAIN', 'example.com');
//// $sub = env('MTA_SUB', 'mx') ?: 'mx';
//// return "{$sub}.{$base}";
//// }
////
//// public function computeHashFromCert(string $host): ?string
//// {
//// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
//// if (!is_file($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;
//// }
////
//// /**
//// * Schreibt/aktualisiert TLSA in DB (global) + Datei unter /etc/mailwolt/dns/.
//// * Wir speichern ohne domain_id (global, nur pro Host/Service).
//// */
//// public function refreshForMx(string $service = '_25._tcp'): ?TlsaRecord
//// {
//// $host = $this->resolveMtaHost();
//// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
//// $hash = $this->computeHashFromCert($host);
//// if (!$hash) return null;
////
//// // DB upsert (domain_id = null → globaler Eintrag)
//// $rec = TlsaRecord::updateOrCreate(
//// ['domain_id' => null, 'host' => $host, 'service' => $service],
//// [
//// 'usage' => 3, // DANE-EE
//// 'selector' => 1, // SPKI
//// 'matching' => 1, // SHA-256
//// 'hash' => $hash,
//// 'cert_path' => $certPath,
//// ]
//// );
////
//// // Datei schreiben (für externen DNS-Export etc.)
//// @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;
//// }
////}