270 lines
8.9 KiB
PHP
270 lines
8.9 KiB
PHP
<?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');
|
||
//
|
||
// // 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;
|
||
// }
|
||
//}
|