mailwolt/app/Console/Commands/GenerateTlsaRecord.php

96 lines
3.1 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\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;
}
}