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'); // // // Let’s 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; // } //}