From 4a522842459ee9f791129a97be963a3d26eb432f Mon Sep 17 00:00:00 2001 From: boban Date: Fri, 17 Oct 2025 12:24:31 +0200 Subject: [PATCH] Anpassen der Tlsa Record Ausgabe im Modal --- a?->dns_string); | 14 +++++ app/Livewire/Ui/Domain/DomainDnsList.php | 1 + .../Ui/Domain/Modal/DomainDnsModal.php | 59 ++++++++++++------- app/Models/TlsaRecord.php | 15 ++++- 4 files changed, 65 insertions(+), 24 deletions(-) create mode 100644 a?->dns_string); diff --git a/a?->dns_string); b/a?->dns_string); new file mode 100644 index 0000000..1d9148b --- /dev/null +++ b/a?->dns_string); @@ -0,0 +1,14 @@ += App\Models\TlsaRecord {#6794 + id: 1, + domain_id: 9, + service: "_25._tcp", + host: "mx.nexlab.at", + usage: 3, + selector: 1, + matching: 1, + hash: "0922eee5f6090b241a3f8554a366b3c1adc4088eb1cdffa94ae838c6e580b983", + cert_path: "/etc/ssl/mail/fullchain.pem", + created_at: "2025-10-06 15:24:56", + updated_at: "2025-10-06 15:24:56", + } + diff --git a/app/Livewire/Ui/Domain/DomainDnsList.php b/app/Livewire/Ui/Domain/DomainDnsList.php index 479a60b..51deeb1 100644 --- a/app/Livewire/Ui/Domain/DomainDnsList.php +++ b/app/Livewire/Ui/Domain/DomainDnsList.php @@ -27,6 +27,7 @@ class DomainDnsList extends Component $this->systemDomain = Domain::where('is_system', true)->first(); // $this->userDomains = Domain::where('is_system', false)->orderBy('domain')->get(); $domains = Domain::where('is_system', false) + ->where('is_server', false) ->withCount([ 'mailUsers as mailboxes_count', // -> mail_users 'mailAliases as aliases_count', // -> mail_aliases diff --git a/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php b/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php index 50dc238..fc153f1 100644 --- a/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php +++ b/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php @@ -3,6 +3,7 @@ namespace App\Livewire\Ui\Domain\Modal; use App\Models\Domain; +use App\Models\TlsaRecord; use App\Support\NetProbe; use Illuminate\Support\Facades\DB; use LivewireUI\Modal\ModalComponent; @@ -20,7 +21,10 @@ class DomainDnsModal extends ModalComponent /** @var array> */ public array $dynamic = []; - public static function modalMaxWidth(): string { return '6xl'; } + public static function modalMaxWidth(): string + { + return '6xl'; + } public function mount(int $domainId): void { @@ -38,43 +42,53 @@ class DomainDnsModal extends ModalComponent $d = Domain::findOrFail($domainId); $this->domainName = $d->domain; - $ips = NetProbe::resolve(); + $ips = NetProbe::resolve(); $ipv4 = $ips['ipv4']; $ipv6 = $ips['ipv6']; $this->zone = $this->extractZone($d->domain); - $mta = env('MTA_SUB', 'mx').'.'.$this->zone; // mx.example.com + $mta = env('MTA_SUB', 'mx') . '.' . $this->zone; // mx.example.com // --- Statische Infrastruktur (für alle Domains gleich) --- $this->static = [ - ['type'=>'A','name'=>$mta,'value'=>$ipv4], - ['type'=>'PTR','name'=>$this->ptrFromIPv4($ipv4),'value'=>$mta], + ['type' => 'A', 'name' => $mta, 'value' => $ipv4], + ['type' => 'PTR', 'name' => $this->ptrFromIPv4($ipv4), 'value' => $mta], ]; if ($ipv6) { - $this->static[] = ['type'=>'AAAA','name'=>$mta,'value'=>$ipv6]; - $this->static[] = ['type'=>'PTR', 'name'=>$this->ptrFromIPv6($ipv6),'value'=>$mta]; + $this->static[] = ['type' => 'AAAA', 'name' => $mta, 'value' => $ipv6]; + $this->static[] = ['type' => 'PTR', 'name' => $this->ptrFromIPv6($ipv6), 'value' => $mta]; } - if ($tlsa = config('mailwolt.tlsa')) { - $this->static[] = ['type'=>'TLSA','name'=>"_25._tcp.$mta",'value'=>$tlsa]; + + + $tlsa = TlsaRecord::forServer() + ->where('service', '_25._tcp') + ->latest('id') + ->first(); + + if ($tlsa?->dns_string) { + $this->static[] = [ + 'type' => 'TLSA', + 'name' => "{$tlsa->service}.{$tlsa->host}", + 'value' => "{$tlsa->usage} {$tlsa->selector} {$tlsa->matching} {$tlsa->hash}", + ]; } - $this->static[] = ['type'=>'MX','name'=>$this->domainName,'value'=>"10 $mta."]; // --- Domain-spezifisch --- - $spf = 'v=spf1 mx a -all'; + $spf = 'v=spf1 mx a -all'; $dmarc = "v=DMARC1; p=none; rua=mailto:dmarc@{$this->domainName}; pct=100"; - +* $dkim = DB::table('dkim_keys') ->where('domain_id', $d->id)->where('is_active', 1)->orderByDesc('id')->first(); $selector = $dkim ? $dkim->selector : 'mwl1'; $dkimHost = "{$selector}._domainkey.{$this->domainName}"; - $dkimTxt = $dkim && !str_starts_with(trim($dkim->public_key_txt),'v=') - ? 'v=DKIM1; k=rsa; p='.$dkim->public_key_txt + $dkimTxt = $dkim && !str_starts_with(trim($dkim->public_key_txt), 'v=') + ? 'v=DKIM1; k=rsa; p=' . $dkim->public_key_txt : ($dkim->public_key_txt ?? 'v=DKIM1; k=rsa; p='); $this->dynamic = [ - ['type'=>'TXT','name'=>$this->domainName, 'value'=>$spf, 'helpLabel'=>'SPF Record Syntax','helpUrl'=>'http://www.open-spf.org/SPF_Record_Syntax/'], - ['type'=>'TXT','name'=>"_dmarc.{$this->domainName}", 'value'=>$dmarc, 'helpLabel'=>'DMARC Assistant','helpUrl'=>'https://www.kitterman.com/dmarc/assistant.html'], - ['type'=>'TXT','name'=>$dkimHost, 'value'=>$dkimTxt, 'helpLabel'=>'DKIM Inspector','helpUrl'=>'https://dkimvalidator.com/'], + ['type' => 'TXT', 'name' => $this->domainName, 'value' => $spf, 'helpLabel' => 'SPF Record Syntax', 'helpUrl' => 'http://www.open-spf.org/SPF_Record_Syntax/'], + ['type' => 'TXT', 'name' => "_dmarc.{$this->domainName}", 'value' => $dmarc, 'helpLabel' => 'DMARC Assistant', 'helpUrl' => 'https://www.kitterman.com/dmarc/assistant.html'], + ['type' => 'TXT', 'name' => $dkimHost, 'value' => $dkimTxt, 'helpLabel' => 'DKIM Inspector', 'helpUrl' => 'https://dkimvalidator.com/'], ]; } @@ -83,14 +97,14 @@ class DomainDnsModal extends ModalComponent $fqdn = strtolower(trim($fqdn, ".")); $parts = explode('.', $fqdn); $n = count($parts); - return $n >= 2 ? $parts[$n-2] . '.' . $parts[$n-1] : $fqdn; // nimmt die letzten 2 Labels + return $n >= 2 ? $parts[$n - 2] . '.' . $parts[$n - 1] : $fqdn; // nimmt die letzten 2 Labels } private function detectIPv4(): string { // robust & ohne env $out = @shell_exec("ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if(\$i==\"src\"){print \$(i+1); exit}}'"); - $ip = trim((string)$out); + $ip = trim((string)$out); if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) return $ip; $ip = trim($_SERVER['SERVER_ADDR'] ?? ''); return $ip ?: '203.0.113.10'; // Fallback Demo @@ -99,20 +113,21 @@ class DomainDnsModal extends ModalComponent private function detectIPv6(): ?string { $out = @shell_exec("ip -6 route get 2001:4860:4860::8888 2>/dev/null | awk '{for(i=1;i<=NF;i++) if(\$i==\"src\"){print \$(i+1); exit}}'"); - $ip = trim((string)$out); + $ip = trim((string)$out); return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? $ip : null; } private function ptrFromIPv4(string $ip): string { $p = array_reverse(explode('.', $ip)); - return implode('.', $p).'.in-addr.arpa'; + return implode('.', $p) . '.in-addr.arpa'; } + private function ptrFromIPv6(string $ip): string { $expanded = strtolower(inet_ntop(inet_pton($ip))); $hex = str_replace(':', '', $expanded); - return implode('.', array_reverse(str_split($hex))).'.ip6.arpa'; + return implode('.', array_reverse(str_split($hex))) . '.ip6.arpa'; } public function render() diff --git a/app/Models/TlsaRecord.php b/app/Models/TlsaRecord.php index ea16ca8..326a465 100644 --- a/app/Models/TlsaRecord.php +++ b/app/Models/TlsaRecord.php @@ -2,16 +2,27 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class TlsaRecord extends Model { protected $fillable = [ - 'domain_id','service','host','usage','selector','matching','hash','cert_path', + 'domain_id', 'service', 'host', 'usage', 'selector', 'matching', 'hash', 'cert_path', ]; - public function domain() { return $this->belongsTo(Domain::class); } + // Relation zur Domain (FK: domain_id) + public function domain() + { + return $this->belongsTo(Domain::class); + } + public function scopeForServer(Builder $q): Builder + { + return $q->whereHas('domain', fn($d) => $d->where('is_server', true)); + } + + // Fertige Ausgabe-Zeile public function getDnsStringAttribute(): string { return "{$this->service}.{$this->host}. IN TLSA {$this->usage} {$this->selector} {$this->matching} {$this->hash}";