mailwolt/app/Console/Commands/GenerateTlsaRecord.php

270 lines
8.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Console\Commands;
use App\Models\Domain;
use App\Services\TlsaService;
use Illuminate\Console\Command;
class GenerateTlsaRecord extends Command
{
protected $signature = 'dns:tlsa
{--domainId= : ID der Domain (optional, alternativ --domainName)}
{--domainName= : Domain-Name wie in domains.name}
{--host= : expliziter MTA-FQDN; sonst per Service/ENV ermittelt}
{--service=_25._tcp}
{--usage=3}
{--selector=1}
{--matching=1}
{--no-file : TLSA nicht als Datei schreiben}';
protected $description = 'Erzeugt/aktualisiert TLSA (3 1 1) in DB (und optional Datei)';
public function handle(TlsaService $tlsa): int
{
$domain = $this->resolveDomain();
if (!$domain) {
$this->error('Domain nicht gefunden (nutze --domainId oder --domainName).');
return self::FAILURE;
}
$host = trim($this->option('host') ?: $tlsa->resolveMtaHost($domain));
$service = $this->option('service') ?: '_25._tcp';
$usage = (int)$this->option('usage');
$selector = (int)$this->option('selector');
$matching = (int)$this->option('matching');
$write = !$this->option('no-file');
$record = $tlsa->upsertTlsa($domain, $host, $service, $usage, $selector, $matching, $write);
if (!$record) {
$this->warn("TLSA konnte nicht erzeugt werden (Zertifikat fehlt?): {$host}");
return self::FAILURE;
}
$this->info("✅ TLSA gespeichert: {$service}.{$host} (3 1 1 {$record->hash})");
return self::SUCCESS;
}
private function resolveDomain(): ?Domain
{
$id = $this->option('domainId');
$name = $this->option('domainName');
if ($id) return Domain::find($id);
if ($name) return Domain::where('name', $name)->first();
// Fallback: erste Domain
return Domain::first();
}
}
//namespace App\Console\Commands;
//
//use App\Models\Domain;
//use App\Models\TlsaRecord;
//use Illuminate\Console\Command;
//use Symfony\Component\Process\Process;
//
//class GenerateTlsaRecord extends Command
//{
// protected $signature = 'dns:tlsa
// {domainId : ID der Domain in eurer domains-Tabelle}
// {--host= : FQDN des MTA-Hosts (z.B. mx.domain.com)}
// {--service=_25._tcp : TLSA Service-Präfix, Standard _25._tcp}
// {--usage=3 : 3 = DANE-EE}
// {--selector=1 : 1 = SPKI}
// {--matching=1 : 1 = SHA-256}';
//
// protected $description = 'Liest vorhandene TLSA-Datei oder erzeugt neuen Eintrag und speichert ihn in tlsa_records.';
//
// public function handle(): int
// {
// $domain = Domain::find($this->argument('domainId'));
// if (!$domain) {
// $this->error('Domain nicht gefunden.');
// return self::FAILURE;
// }
//
// $host = trim($this->option('host'));
// $service = $this->option('service') ?: '_25._tcp';
// $usage = (int)$this->option('usage');
// $selector = (int)$this->option('selector');
// $matching = (int)$this->option('matching');
//
// $dnsDir = '/etc/mailwolt/dns';
// $file = "{$dnsDir}/{$host}.tlsa.txt";
// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
//
// // Prüfen ob bereits Datei existiert
// if (is_file($file)) {
// $this->info("📄 TLSA-Datei gefunden: {$file}");
// $line = trim(file_get_contents($file));
//
// if (preg_match('/IN TLSA (\d) (\d) (\d) ([A-Fa-f0-9]+)/', $line, $m)) {
// $usage = (int)$m[1];
// $selector = (int)$m[2];
// $matching = (int)$m[3];
// $hash = $m[4];
// } else {
// $this->warn("Ungültiges TLSA-Format in {$file}, regeneriere …");
// $hash = $this->generateHash($certPath);
// }
// } else {
// $this->info("🧩 Keine TLSA-Datei gefunden, generiere neu …");
// $hash = $this->generateHash($certPath);
//
// if (!is_dir($dnsDir)) {
// mkdir($dnsDir, 0755, true);
// }
// file_put_contents($file, sprintf(
// "%s.%s IN TLSA %d %d %d %s\n",
// $service,
// $host,
// $usage,
// $selector,
// $matching,
// $hash
// ));
// }
//
// // In DB speichern
// $record = TlsaRecord::updateOrCreate(
// [
// 'domain_id' => $domain->id,
// 'host' => $host,
// 'service' => $service,
// ],
// [
// 'usage' => $usage,
// 'selector' => $selector,
// 'matching' => $matching,
// 'hash' => $hash,
// 'cert_path' => $certPath,
// ]
// );
//
// $this->info("✅ TLSA gespeichert für {$host}");
// return self::SUCCESS;
// }
//
// private function generateHash(string $certPath): string
// {
// if (!is_file($certPath)) {
// throw new \RuntimeException("Zertifikat nicht gefunden: {$certPath}");
// }
//
// $cmd = sprintf(
// 'openssl x509 -in %s -noout -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256',
// escapeshellarg($certPath)
// );
// $proc = Process::fromShellCommandline($cmd);
// $proc->run();
//
// if (!$proc->isSuccessful()) {
// throw new \RuntimeException('Fehler bei der Hash-Erzeugung: ' . $proc->getErrorOutput());
// }
//
// return preg_replace('/^SHA256\(stdin\)=\s*/', '', trim($proc->getOutput()));
// }
//}
//
//-----
//
//namespace App\Console\Commands;
//
//use App\Models\Domain;
//use App\Models\TlsaRecord;
//use App\Support\Hostnames;
//use Illuminate\Console\Command;
//use Symfony\Component\Process\Process;
//
//class GenerateTlsaRecord extends Command
//{
// protected $signature = 'dns:tlsa
// {domainId : ID der Domain in eurer domains-Tabelle}
// {--host= : FQDN des MTA-Hosts (z.B. mailsrv012.domain.com)}
// {--service=_25._tcp : TLSA Service-Präfix, Standard _25._tcp}
// {--usage=3 : 3 = DANE-EE}
// {--selector=1 : 1 = SPKI}
// {--matching=1 : 1 = SHA-256}';
//
// protected $description = 'Erzeugt/aktualisiert einen TLSA-Record und speichert ihn in tlsa_records.';
//
// public function handle(): int
// {
// $domain = Domain::find($this->argument('domainId'));
// if (!$domain) {
// $this->error('Domain nicht gefunden.');
// return self::FAILURE;
// }
//
// // Host bestimmen
// $host = trim($this->option('host') ?: Hostnames::mta());
// if (!str_contains($host, '.')) {
// $this->error("Ungültiger Host: {$host}");
// return self::FAILURE;
// }
//
// $service = $this->option('service') ?: '_25._tcp';
// $usage = (int) $this->option('usage');
// $selector = (int) $this->option('selector');
// $matching = (int) $this->option('matching');
//
// // Lets Encrypt Pfad (ggf. anpassen, falls anderes CA/Verzeichnis)
// $certPath = "/etc/letsencrypt/live/{$host}/fullchain.pem";
//
// if (!is_file($certPath)) {
// $this->error("Zertifikat nicht gefunden: {$certPath}");
// $this->line('Tipp: LE deploy hook/renewal erst durchlaufen lassen oder Pfad anpassen.');
// return self::FAILURE;
// }
//
// // Hash über SPKI (selector=1) + SHA-256 (matching=1)
// $cmd = "openssl x509 -in ".escapeshellarg($certPath)." -noout -pubkey"
// . " | openssl pkey -pubin -outform DER"
// . " | openssl dgst -sha256";
// $proc = Process::fromShellCommandline($cmd);
// $proc->run();
//
// if (!$proc->isSuccessful()) {
// $this->error('Fehler bei der Hash-Erzeugung (openssl).');
// $this->line($proc->getErrorOutput());
// return self::FAILURE;
// }
//
// $hash = preg_replace('/^SHA256\(stdin\)=\s*/', '', trim($proc->getOutput()));
//
// $record = TlsaRecord::updateOrCreate(
// [
// 'domain_id' => $domain->id,
// 'host' => $host,
// 'service' => $service,
// ],
// [
// 'usage' => $usage,
// 'selector' => $selector,
// 'matching' => $matching,
// 'hash' => $hash,
// 'cert_path' => $certPath,
// ]
// );
//
// $this->info('✅ TLSA gespeichert');
// $this->line(sprintf(
// '%s.%s IN TLSA %d %d %d %s',
// $record->service,
// $record->host,
// $record->usage,
// $record->selector,
// $record->matching,
// $record->hash
// ));
//
// return self::SUCCESS;
// }
//}