Anpassen der Tlsa Record erstellung

main
boban 2025-10-17 06:18:27 +02:00
parent 7fd76f3b32
commit 3ee53a0ae5
1 changed files with 105 additions and 19 deletions

View File

@ -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.<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";
@ -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');