From 3c1093311c12a40482956a0aa1bd88a36a7ba59d Mon Sep 17 00:00:00 2001 From: boban Date: Wed, 29 Oct 2025 04:10:49 +0100 Subject: [PATCH] =?UTF-8?q?Fix:=20Mailbox=20Stats=20=C3=BCber=20Dovecot=20?= =?UTF-8?q?mit=20config/mailpool.php?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Ui/Domain/Modal/DomainDnsModal.php | 9 +- app/Livewire/Ui/Mail/DnsHealthCard.php | 201 +++++++++++++++--- .../ui/mail/dns-health-card.blade.php | 72 +++++-- 3 files changed, 233 insertions(+), 49 deletions(-) diff --git a/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php b/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php index 672ba25..fea3170 100644 --- a/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php +++ b/app/Livewire/Ui/Domain/Modal/DomainDnsModal.php @@ -103,13 +103,6 @@ class DomainDnsModal extends ModalComponent ['type' => 'TXT', 'name' => $dkimHost, 'value' => $dkimTxt], ]; -// $this->optional = [ -// ['type' => 'SRV', 'name' => "_autodiscover._tcp.{$this->domainName}", 'value' => "0 1 443 {$mailServerFqdn}."], -// ['type' => 'SRV', 'name' => "_imaps._tcp.{$this->domainName}", 'value' => "0 1 993 {$mailServerFqdn}."], -// ['type' => 'SRV', 'name' => "_pop3s._tcp.{$this->domainName}", 'value' => "0 1 995 {$mailServerFqdn}."], -// ['type' => 'SRV', 'name' => "_submission._tcp.{$this->domainName}", 'value' => "0 1 587 {$mailServerFqdn}."], -// ]; - $this->optional = [ // --- Service-Erkennung --- [ @@ -587,6 +580,8 @@ class DomainDnsModal extends ModalComponent return view('livewire.ui.domain.modal.domain-dns-modal'); } } + + //namespace App\Livewire\Ui\Domain\Modal; // //use App\Models\Domain; diff --git a/app/Livewire/Ui/Mail/DnsHealthCard.php b/app/Livewire/Ui/Mail/DnsHealthCard.php index f284583..7d8c04a 100644 --- a/app/Livewire/Ui/Mail/DnsHealthCard.php +++ b/app/Livewire/Ui/Mail/DnsHealthCard.php @@ -2,16 +2,16 @@ namespace App\Livewire\Ui\Mail; -use Livewire\Attributes\On; use Livewire\Component; use App\Models\Domain; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\DB; class DnsHealthCard extends Component { public array $rows = []; // [{id,name,ok,missing:[...]}] - public string $mtaHost = ''; // z.B. mx.nexlab.at - public bool $tlsa = false; // hostweit + public string $mtaHost = ''; + public bool $tlsa = false; public function mount(): void { @@ -35,51 +35,66 @@ class DnsHealthCard extends Component protected function load(bool $force = false): void { - [$this->mtaHost, $this->tlsa, $this->rows] = Cache::remember('dash.dnshealth.v1', $force ? 1 : 600, function () { + [$this->mtaHost, $this->tlsa, $this->rows] = Cache::remember('dash.dnshealth.v2', $force ? 1 : 600, function () { - $base = trim((string)env('BASE_DOMAIN', '')); - $mtaSub = trim((string)env('MTA_SUB', 'mx')); - $mtaHost = $base !== '' ? "{$mtaSub}.{$base}" : $mtaSub; + $base = trim((string) env('BASE_DOMAIN', '')); + $mtaSub = trim((string) env('MTA_SUB', 'mx')); + $mtaHost = $base !== '' ? "{$mtaSub}.{$base}" : $mtaSub; // z.B. mx.nexlab.at - $rows = []; + // ▼ gewünschter Filter: $domains = Domain::query() - ->where('is_system', false) ->where('is_active', true) + ->where(function ($q) { + $q->where('is_system', false) + ->orWhere(function ($q2) { + $q2->where('is_system', true)->where('is_server', false); + }); + }) ->orderBy('domain') ->get(['id', 'domain']); + $rows = []; foreach ($domains as $d) { $dom = $d->domain; + // DKIM-Selector ermitteln: .env > DB > Fallback null + $selector = trim((string) env('DKIM_SELECTOR', '')); + if ($selector === '') { + $selector = (string) DB::table('dkim_keys') + ->where('domain_id', $d->id) + ->where('is_active', 1) + ->orderByDesc('id') + ->value('selector') ?? ''; + } + $missing = []; - // Pflicht-Checks if (!$this->mxPointsTo($dom, [$mtaHost])) $missing[] = 'MX'; - if (!$this->hasSpf($dom)) $missing[] = 'SPF'; - if (!$this->hasDkim($dom)) $missing[] = 'DKIM'; - if (!$this->hasTxt("_dmarc.$dom")) $missing[] = 'DMARC'; + if (!$this->hasSpf($dom)) $missing[] = 'SPF'; + if (!$this->hasDkim($dom, $selector)) $missing[] = 'DKIM'; + if (!$this->hasTxt("_dmarc.$dom")) $missing[] = 'DMARC'; $rows[] = [ - 'id' => (int)$d->id, - 'name' => $dom, - 'ok' => empty($missing), + 'id' => (int) $d->id, + 'name' => $dom, + 'ok' => empty($missing), 'missing' => $missing, ]; } - // TLSA (hostweit, nur Info) + // Hostweites TLSA (nur Hinweis) $tlsa = $this->hasTlsa("_25._tcp.$mtaHost") || $this->hasTlsa("_465._tcp.$mtaHost") || $this->hasTlsa("_587._tcp.$mtaHost"); return [$mtaHost, $tlsa, $rows]; }); } - /* ── DNS Helpers (mit Timeout, damit UI nicht hängt) ───────────────── */ + /* ── DNS Helpers ───────────────────────────────────────────────────── */ protected function digShort(string $type, string $name): string { - $cmd = "timeout 2 dig +short " . escapeshellarg($name) . " " . escapeshellarg(strtoupper($type)) . " 2>/dev/null"; - return (string)@shell_exec($cmd) ?: ''; + $cmd = "timeout 2 dig +short " . escapeshellarg($name) . ' ' . escapeshellarg(strtoupper($type)) . " 2>/dev/null"; + return (string) @shell_exec($cmd) ?: ''; } protected function hasTxt(string $name): bool @@ -101,11 +116,13 @@ class DnsHealthCard extends Component return false; } - // DKIM: wenn spezifischer Selector vorhanden → prüfe den, sonst akzeptiere _domainkey-Policy als “vorhanden” - protected function hasDkim(string $domain): bool + // ▼ DKIM: bevorzugt konkreten Selector prüfen; wenn leer, versuche Policy (_domainkey) + protected function hasDkim(string $domain, string $selector = ''): bool { - $sel = trim((string)env('DKIM_SELECTOR', '')); - if ($sel !== '' && $this->hasTxt("{$sel}._domainkey.$domain")) return true; + if ($selector !== '') { + return $this->hasTxt("{$selector}._domainkey.$domain"); + } + // Manche Betreiber veröffentlichen eine Policy auf _domainkey. return $this->hasTxt("_domainkey.$domain"); } @@ -116,21 +133,153 @@ class DnsHealthCard extends Component $targets = []; foreach (preg_split('/\R+/', trim($out)) as $line) { - // Format: "10 mx.example.com." if (preg_match('~\s+([A-Za-z0-9\.\-]+)\.?$~', trim($line), $m)) { $targets[] = strtolower($m[1]); } } if (!$targets) return false; - $allowed = array_map('strtolower', $allowedHosts); + $allowed = array_map(fn ($h) => strtolower(rtrim($h, '.')), $allowedHosts); foreach ($targets as $t) { + $t = rtrim($t, '.'); if (in_array($t, $allowed, true)) return true; } return false; } } +//namespace App\Livewire\Ui\Mail; +// +//use Livewire\Attributes\On; +//use Livewire\Component; +//use App\Models\Domain; +//use Illuminate\Support\Facades\Cache; +// +//class DnsHealthCard extends Component +//{ +// public array $rows = []; // [{id,name,ok,missing:[...]}] +// public string $mtaHost = ''; // z.B. mx.nexlab.at +// public bool $tlsa = false; // hostweit +// +// public function mount(): void +// { +// $this->load(); +// } +// +// public function render() +// { +// return view('livewire.ui.mail.dns-health-card'); +// } +// +// public function refresh(): void +// { +// $this->load(true); +// } +// +// public function openDnsModal(int $domainId): void +// { +// $this->dispatch('openModal', component: 'ui.domain.modal.domain-dns-modal', arguments: ['domainId' => $domainId]); +// } +// +// protected function load(bool $force = false): void +// { +// [$this->mtaHost, $this->tlsa, $this->rows] = Cache::remember('dash.dnshealth.v1', $force ? 1 : 600, function () { +// +// $base = trim((string)env('BASE_DOMAIN', '')); +// $mtaSub = trim((string)env('MTA_SUB', 'mx')); +// $mtaHost = $base !== '' ? "{$mtaSub}.{$base}" : $mtaSub; +// +// $rows = []; +// $domains = Domain::query() +// ->where('is_system', false) +// ->where('is_active', true) +// ->orderBy('domain') +// ->get(['id', 'domain']); +// +// foreach ($domains as $d) { +// $dom = $d->domain; +// +// $missing = []; +// +// // Pflicht-Checks +// if (!$this->mxPointsTo($dom, [$mtaHost])) $missing[] = 'MX'; +// if (!$this->hasSpf($dom)) $missing[] = 'SPF'; +// if (!$this->hasDkim($dom)) $missing[] = 'DKIM'; +// if (!$this->hasTxt("_dmarc.$dom")) $missing[] = 'DMARC'; +// +// $rows[] = [ +// 'id' => (int)$d->id, +// 'name' => $dom, +// 'ok' => empty($missing), +// 'missing' => $missing, +// ]; +// } +// +// // TLSA (hostweit, nur Info) +// $tlsa = $this->hasTlsa("_25._tcp.$mtaHost") || $this->hasTlsa("_465._tcp.$mtaHost") || $this->hasTlsa("_587._tcp.$mtaHost"); +// +// return [$mtaHost, $tlsa, $rows]; +// }); +// } +// +// /* ── DNS Helpers (mit Timeout, damit UI nicht hängt) ───────────────── */ +// +// protected function digShort(string $type, string $name): string +// { +// $cmd = "timeout 2 dig +short " . escapeshellarg($name) . " " . escapeshellarg(strtoupper($type)) . " 2>/dev/null"; +// return (string)@shell_exec($cmd) ?: ''; +// } +// +// protected function hasTxt(string $name): bool +// { +// return trim($this->digShort('TXT', $name)) !== ''; +// } +// +// protected function hasTlsa(string $name): bool +// { +// return trim($this->digShort('TLSA', $name)) !== ''; +// } +// +// protected function hasSpf(string $domain): bool +// { +// $out = $this->digShort('TXT', $domain); +// foreach (preg_split('/\R+/', trim($out)) as $line) { +// if (stripos($line, 'v=spf1') !== false) return true; +// } +// return false; +// } +// +// // DKIM: wenn spezifischer Selector vorhanden → prüfe den, sonst akzeptiere _domainkey-Policy als “vorhanden” +// protected function hasDkim(string $domain): bool +// { +// $sel = trim((string)env('DKIM_SELECTOR', '')); +// if ($sel !== '' && $this->hasTxt("{$sel}._domainkey.$domain")) return true; +// return $this->hasTxt("_domainkey.$domain"); +// } +// +// protected function mxPointsTo(string $domain, array $allowedHosts): bool +// { +// $out = $this->digShort('MX', $domain); +// if ($out === '') return false; +// +// $targets = []; +// foreach (preg_split('/\R+/', trim($out)) as $line) { +// // Format: "10 mx.example.com." +// if (preg_match('~\s+([A-Za-z0-9\.\-]+)\.?$~', trim($line), $m)) { +// $targets[] = strtolower($m[1]); +// } +// } +// if (!$targets) return false; +// +// $allowed = array_map('strtolower', $allowedHosts); +// foreach ($targets as $t) { +// if (in_array($t, $allowed, true)) return true; +// } +// return false; +// } +//} + + //namespace App\Livewire\Ui\Mail; // //use Livewire\Component; diff --git a/resources/views/livewire/ui/mail/dns-health-card.blade.php b/resources/views/livewire/ui/mail/dns-health-card.blade.php index f374ee4..37591d2 100644 --- a/resources/views/livewire/ui/mail/dns-health-card.blade.php +++ b/resources/views/livewire/ui/mail/dns-health-card.blade.php @@ -1,15 +1,17 @@ -
+
Mail-DNS Health
-
- TLSA: - - {{ $tlsa ? 'ok' : 'fehlend' }} - - ({{ $mtaHost }}) +
+
@@ -17,7 +19,7 @@ @forelse($rows as $r) @empty @@ -41,6 +38,49 @@
+{{--
--}} +{{--
--}} +{{--
--}} +{{-- --}} +{{-- Mail-DNS Health--}} +{{--
--}} +{{--
--}} +{{-- TLSA:--}} +{{-- --}} +{{-- {{ $tlsa ? 'ok' : 'fehlend' }}--}} +{{-- --}} +{{-- ({{ $mtaHost }})--}} +{{--
--}} +{{--
--}} + +{{--
--}} +{{-- @forelse($rows as $r)--}} +{{-- --}} +{{-- @empty--}} +{{--
Keine Domains.
--}} +{{-- @endforelse--}} +{{--
--}} +{{--
--}} + {{--
--}} {{--
--}} {{--
--}}