Fix: Mailbox Stats über Dovecot mit config/mailpool.php

main v1.0.90
boban 2025-10-28 22:53:41 +01:00
parent 2ef27b5e8f
commit c8cae445c5
2 changed files with 253 additions and 69 deletions

View File

@ -1,10 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Livewire\Ui\Security;
use Livewire\Component;
use Illuminate\Support\Facades\Cache;
use Livewire\Component;
class RblCard extends Component
{
@ -15,6 +15,9 @@ class RblCard extends Component
public ?string $ipv4 = null;
public ?string $ipv6 = null;
// Schalte registrierungspflichtige Listen (Barracuda etc.) optional zu
private bool $includeRegistered = false; // env('RBL_INCLUDE_REGISTERED', false) wenn du willst
public function mount(): void
{
$this->load();
@ -33,33 +36,21 @@ class RblCard extends Component
protected function load(bool $force = false): void
{
// 1) IPv4/IPv6 bevorzugt aus /etc/mailwolt/installer.env
[$ip4, $ip6] = $this->resolvePublicIpsFromInstallerEnv();
// 2) Fallback auf .env
$this->ipv4 = $ip4 ?: trim((string) env('SERVER_PUBLIC_IPV4', '')) ?: '';
$this->ipv6 = $ip6 ?: trim((string) env('SERVER_PUBLIC_IPV6', '')) ?: '';
$this->ipv4 = $ip4 ?: trim((string)env('SERVER_PUBLIC_IPV4', '')) ?: '';
$this->ipv6 = $ip6 ?: trim((string)env('SERVER_PUBLIC_IPV6', '')) ?: '';
// 3) RBL-Ermittlung (cached)
$data = Cache::remember('dash.rbl', $force ? 1 : 21600, function () {
// bevorzugt eine valide IPv4 für den RBL-Check
$data = Cache::remember('dash.rbl', $force ? 1 : 6 * 3600, function () {
$candidate = $this->validIPv4($this->ipv4 ?? '') ? $this->ipv4 : null;
if (!$candidate) {
$fromFile = @file_get_contents('/etc/mailwolt/public_ip') ?: '';
$fromFile = trim($fromFile);
if ($this->validIPv4($fromFile)) {
$candidate = $fromFile;
}
$fromFile = trim((string)@file_get_contents('/etc/mailwolt/public_ip'));
if ($this->validIPv4($fromFile)) $candidate = $fromFile;
}
if (!$candidate) {
// letzter Fallback kann auf Hardened-Systemen geblockt sein
$curl = @shell_exec("curl -fsS --max-time 2 ifconfig.me 2>/dev/null") ?: '';
$curl = trim($curl);
if ($this->validIPv4($curl)) {
$candidate = $curl;
}
$curl = trim((string)@shell_exec("curl -fsS --max-time 2 ifconfig.me 2>/dev/null"));
if ($this->validIPv4($curl)) $candidate = $curl;
}
$ip = $candidate ?: '0.0.0.0';
@ -68,82 +59,76 @@ class RblCard extends Component
return ['ip' => $ip, 'hits' => count($lists), 'lists' => $lists];
});
// 4) Werte ins Component-State
foreach ($data as $k => $v) {
$this->$k = $v;
}
foreach ($data as $k => $v) $this->$k = $v;
}
/** Bevorzugt Installer-ENV; gibt [ipv4, ipv6] zurück oder [null, null]. */
/** bevorzugt Installer-ENV */
private function resolvePublicIpsFromInstallerEnv(): array
{
$file = '/etc/mailwolt/installer.env';
if (!is_readable($file)) {
return [null, null];
}
$ipv4 = null;
$ipv6 = null;
if (!is_readable($file)) return [null, null];
$ipv4 = $ipv6 = null;
$lines = @file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
foreach ($lines as $line) {
// Kommentare überspringen
if (preg_match('/^\s*#/', $line)) {
continue;
}
// KEY=VALUE (VALUE evtl. in "..." oder '...')
if (!str_contains($line, '=')) {
continue;
}
if (preg_match('/^\s*#/', $line) || !str_contains($line, '=')) continue;
[$k, $v] = array_map('trim', explode('=', $line, 2));
$v = trim($v, " \t\n\r\0\x0B\"'");
if ($k === 'SERVER_PUBLIC_IPV4' && $this->validIPv4($v)) {
$ipv4 = $v;
} elseif ($k === 'SERVER_PUBLIC_IPV6' && $this->validIPv6($v)) {
$ipv6 = $v;
}
if ($k === 'SERVER_PUBLIC_IPV4' && $this->validIPv4($v)) $ipv4 = $v;
if ($k === 'SERVER_PUBLIC_IPV6' && $this->validIPv6($v)) $ipv6 = $v;
}
return [$ipv4, $ipv6];
}
private function validIPv4(?string $ip): bool
{
return (bool) filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
return (bool)filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
}
private function validIPv6(?string $ip): bool
{
return (bool) filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
return (bool)filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
}
/**
* Prüft die IP gegen ein paar gängige RBLs.
* Nutzt PHP-DNS (checkdnsrr), keine externen Tools.
*
* Prüft die IP gegen gängige **öffentliche** RBLs.
* @return array<string> gelistete RBL-Zonen
*/
private function queryRblLists(string $ip): array
{
// Nur IPv4 prüfen (die meisten Listen hier sind v4)
if (!$this->validIPv4($ip)) {
return [];
}
if (!$this->validIPv4($ip)) return [];
$rev = implode('.', array_reverse(explode('.', $ip)));
$sources = [
'zen.spamhaus.org',
'bl.spamcop.net',
'dnsbl.sorbs.net',
'b.barracudacentral.org',
// Öffentliche/abfragbare Zonen (keine Registrierung nötig)
$publicZones = [
'zen.spamhaus.org', // seriös, rate-limited
'psbl.surriel.com', // public
'dnsbl-1.uceprotect.net', // public (umstritten, aber abfragbar)
'all.s5h.net', // public
];
// Registrierungspflichtige nur optional prüfen
$registeredZones = [
'b.barracudacentral.org', // benötigt Account
// weitere bei Bedarf
];
$zones = $publicZones;
if ($this->includeRegistered) {
$zones = array_merge($zones, $registeredZones);
}
// Vorab: Zone existiert? (NS-Record) sonst überspringen
$zones = array_values(array_filter($zones, function ($z) {
return @checkdnsrr($z . '.', 'NS');
}));
$listed = [];
foreach ($sources as $zone) {
$qname = "{$rev}.{$zone}";
// A-Record oder TXT deuten auf Listing hin
if (@checkdnsrr($qname . '.', 'A') || @checkdnsrr($qname . '.', 'TXT')) {
foreach ($zones as $zone) {
$qname = "{$rev}.{$zone}.";
// Wenn A oder TXT existiert → gelistet
if (@checkdnsrr($qname, 'A') || @checkdnsrr($qname, 'TXT')) {
$listed[] = $zone;
}
}
@ -151,6 +136,158 @@ class RblCard extends Component
return $listed;
}
}
//declare(strict_types=1);
//
//namespace App\Livewire\Ui\Security;
//
//use Livewire\Component;
//use Illuminate\Support\Facades\Cache;
//
//class RblCard extends Component
//{
// public string $ip = '';
// public int $hits = 0;
// public array $lists = [];
//
// public ?string $ipv4 = null;
// public ?string $ipv6 = null;
//
// public function mount(): void
// {
// $this->load();
// }
//
// public function render()
// {
// return view('livewire.ui.security.rbl-card');
// }
//
// public function refresh(): void
// {
// Cache::forget('dash.rbl');
// $this->load(true);
// }
//
// protected function load(bool $force = false): void
// {
// // 1) IPv4/IPv6 bevorzugt aus /etc/mailwolt/installer.env
// [$ip4, $ip6] = $this->resolvePublicIpsFromInstallerEnv();
//
// // 2) Fallback auf .env
// $this->ipv4 = $ip4 ?: trim((string) env('SERVER_PUBLIC_IPV4', '')) ?: '';
// $this->ipv6 = $ip6 ?: trim((string) env('SERVER_PUBLIC_IPV6', '')) ?: '';
//
// // 3) RBL-Ermittlung (cached)
// $data = Cache::remember('dash.rbl', $force ? 1 : 21600, function () {
// // bevorzugt eine valide IPv4 für den RBL-Check
// $candidate = $this->validIPv4($this->ipv4 ?? '') ? $this->ipv4 : null;
//
// if (!$candidate) {
// $fromFile = @file_get_contents('/etc/mailwolt/public_ip') ?: '';
// $fromFile = trim($fromFile);
// if ($this->validIPv4($fromFile)) {
// $candidate = $fromFile;
// }
// }
//
// if (!$candidate) {
// // letzter Fallback kann auf Hardened-Systemen geblockt sein
// $curl = @shell_exec("curl -fsS --max-time 2 ifconfig.me 2>/dev/null") ?: '';
// $curl = trim($curl);
// if ($this->validIPv4($curl)) {
// $candidate = $curl;
// }
// }
//
// $ip = $candidate ?: '0.0.0.0';
// $lists = $this->queryRblLists($ip);
//
// return ['ip' => $ip, 'hits' => count($lists), 'lists' => $lists];
// });
//
// // 4) Werte ins Component-State
// foreach ($data as $k => $v) {
// $this->$k = $v;
// }
// }
//
// /** Bevorzugt Installer-ENV; gibt [ipv4, ipv6] zurück oder [null, null]. */
// private function resolvePublicIpsFromInstallerEnv(): array
// {
// $file = '/etc/mailwolt/installer.env';
// if (!is_readable($file)) {
// return [null, null];
// }
//
// $ipv4 = null;
// $ipv6 = null;
//
// $lines = @file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
// foreach ($lines as $line) {
// // Kommentare überspringen
// if (preg_match('/^\s*#/', $line)) {
// continue;
// }
// // KEY=VALUE (VALUE evtl. in "..." oder '...')
// if (!str_contains($line, '=')) {
// continue;
// }
// [$k, $v] = array_map('trim', explode('=', $line, 2));
// $v = trim($v, " \t\n\r\0\x0B\"'");
//
// if ($k === 'SERVER_PUBLIC_IPV4' && $this->validIPv4($v)) {
// $ipv4 = $v;
// } elseif ($k === 'SERVER_PUBLIC_IPV6' && $this->validIPv6($v)) {
// $ipv6 = $v;
// }
// }
//
// return [$ipv4, $ipv6];
// }
//
// private function validIPv4(?string $ip): bool
// {
// return (bool) filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
// }
//
// private function validIPv6(?string $ip): bool
// {
// return (bool) filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
// }
//
// /**
// * Prüft die IP gegen ein paar gängige RBLs.
// * Nutzt PHP-DNS (checkdnsrr), keine externen Tools.
// *
// * @return array<string> gelistete RBL-Zonen
// */
// private function queryRblLists(string $ip): array
// {
// // Nur IPv4 prüfen (die meisten Listen hier sind v4)
// if (!$this->validIPv4($ip)) {
// return [];
// }
//
// $rev = implode('.', array_reverse(explode('.', $ip)));
// $sources = [
// 'zen.spamhaus.org',
// 'bl.spamcop.net',
// 'dnsbl.sorbs.net',
// 'b.barracudacentral.org',
// ];
//
// $listed = [];
// foreach ($sources as $zone) {
// $qname = "{$rev}.{$zone}";
// // A-Record oder TXT deuten auf Listing hin
// if (@checkdnsrr($qname . '.', 'A') || @checkdnsrr($qname . '.', 'TXT')) {
// $listed[] = $zone;
// }
// }
//
// return $listed;
// }
//}
//
//namespace App\Livewire\Ui\Security;
//

View File

@ -4,17 +4,64 @@
<i class="ph ph-shield-warning text-white/70 text-[13px]"></i>
<span class="text-[11px] uppercase text-white/70">Reputation / RBL</span>
</div>
<div class="text-xs text-white/70">IP: <span class="text-white/90">{{ $ip }}</span></div>
<div class="text-xs text-white/70">
IP: <span class="text-white/90">{{ $ip }}</span>
</div>
</div>
<div class="mt-3">
@if($hits === 0)
<span class="px-2 py-0.5 rounded-full border text-emerald-300 border-emerald-400/30 bg-emerald-500/10">Keine Treffer</span>
<div class="flex items-center gap-2 text-emerald-300">
<i class="ph ph-shield-check-thin text-lg"></i>
<span>Deine IP ist aktuell auf keiner der geprüften Blacklists.</span>
</div>
<div class="mt-2 text-[11px] text-white/45">
Geprüfte öffentliche RBLs: Spamhaus, PSBL, UCEPROTECT-1, s5h.
</div>
@else
<div class="text-sm text-white/70">{{ $hits }} Treffer:</div>
<div class="flex items-center gap-2 text-rose-300">
<i class="ph ph-warning-circle text-lg"></i>
<span>{{ $hits }} Treffer auf Blacklists:</span>
</div>
<ul class="mt-2 space-y-1 text-sm text-rose-300">
@foreach($lists as $l)<li> {{ $l }}</li>@endforeach
@foreach($lists as $l)
<li class="flex items-center gap-2">
<span class="inline-block w-1.5 h-1.5 rounded-full bg-rose-400"></span>
<span>{{ $l }}</span>
</li>
@endforeach
</ul>
@endif
</div>
<div class="mt-3">
<button wire:click="refresh"
class="inline-flex items-center gap-1.5 rounded-full text-[11px] px-3 py-1
text-white/80 bg-white/10 border border-white/15
hover:bg-white/15 hover:text-white">
<i class="ph ph-arrows-clockwise text-[12px]"></i>
Neu prüfen
</button>
</div>
</div>
{{--<div class="glass-card p-4 rounded-2xl border border-white/10 bg-white/5">--}}
{{-- <div class="flex items-center justify-between">--}}
{{-- <div class="inline-flex items-center gap-2 bg-white/5 border border-white/10 px-2.5 py-1 rounded-full">--}}
{{-- <i class="ph ph-shield-warning text-white/70 text-[13px]"></i>--}}
{{-- <span class="text-[11px] uppercase text-white/70">Reputation / RBL</span>--}}
{{-- </div>--}}
{{-- <div class="text-xs text-white/70">IP: <span class="text-white/90">{{ $ip }}</span></div>--}}
{{-- </div>--}}
{{-- <div class="mt-3">--}}
{{-- @if($hits === 0)--}}
{{-- <span class="px-2 py-0.5 rounded-full border text-emerald-300 border-emerald-400/30 bg-emerald-500/10">Keine Treffer</span>--}}
{{-- @else--}}
{{-- <div class="text-sm text-white/70">{{ $hits }} Treffer:</div>--}}
{{-- <ul class="mt-2 space-y-1 text-sm text-rose-300">--}}
{{-- @foreach($lists as $l)<li> {{ $l }}</li>@endforeach--}}
{{-- </ul>--}}
{{-- @endif--}}
{{-- </div>--}}
{{--</div>--}}