diff --git a/app/Livewire/Ui/Domain/Modal/DomainCreateModal.php b/app/Livewire/Ui/Domain/Modal/DomainCreateModal.php index d7a4dd3..d967e3c 100644 --- a/app/Livewire/Ui/Domain/Modal/DomainCreateModal.php +++ b/app/Livewire/Ui/Domain/Modal/DomainCreateModal.php @@ -35,8 +35,8 @@ class DomainCreateModal extends ModalComponent public bool $active = true; // DKIM - public string $dkim_selector = 'dkim'; - public int $dkim_bits = 2048; // 1024/2048/3072/4096 + public string $dkim_selector; + public int $dkim_bits; // Anzeige public int $available_mib = 0; @@ -52,8 +52,8 @@ class DomainCreateModal extends ModalComponent $this->max_quota_per_mailbox_mb = config('mailpool.defaults.max_quota_per_mailbox_mb', 3072); $this->total_quota_mb = (int)config('mailpool.defaults.total_quota_mb', 10240); - $this->dkim_selector = (string)config('mailpool.defaults.dkim_selector', 'dkim'); - $this->dkim_bits = (int)config('mailpool.defaults.dkim_bits', 2048); + $this->dkim_selector = (string) config('mailpool.defaults.dkim_selector', 'mwl1'); + $this->dkim_bits = (int) config('mailpool.defaults.dkim_bits', 2048); // Speicherpool-Grenze $this->available_mib = (int)$pool->remainingPoolMb(); diff --git a/app/Observers/DomainObserver.php b/app/Observers/DomainObserver.php index dfd4a9c..e951de8 100644 --- a/app/Observers/DomainObserver.php +++ b/app/Observers/DomainObserver.php @@ -16,7 +16,7 @@ class DomainObserver */ public function created(Domain $domain): void { - $selector = (string) config('mailpool.defaults.dkim_selector', 'dkim'); + $selector = (string) config('mailpool.defaults.dkim_selector', 'mwl1'); $bits = (int) config('mailpool.defaults.dkim_bits', 2048); $res = app(\App\Services\DkimService::class) diff --git a/app/Services/DkimService.php b/app/Services/DkimService.php index 0d81cdd..4695809 100644 --- a/app/Services/DkimService.php +++ b/app/Services/DkimService.php @@ -11,20 +11,42 @@ use RuntimeException; class DkimService { /** Erzeugt Keypair & gibt den TXT-Record (ohne Host) zurück. */ - public function generateForDomain(Domain $domainId, int $bits = 2048, string $selector = 'dkim'): array + public function generateForDomain(Domain $domain, int $bits = 2048, string $selector = null): array { - $dirKey = $this->safeKey($domainId); - $selKey = $this->safeKey($selector, 32); + // 1) Selector zentral aus der Config (Fallback 'mwl1') + $selector = $selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1'); - $disk = Storage::disk('local'); - $baseRel = "private/dkim/{$dirKey}"; - $privRel = "{$baseRel}/{$selKey}.pem"; - $pubRel = "{$baseRel}/{$selKey}.pub"; + $dirKey = $this->safeKey($domain); + $selKey = $this->safeKey($selector, 32); - // 1) Ordner sicherstellen + $disk = Storage::disk('local'); + $baseRel = "private/dkim/{$dirKey}"; + $privRel = "{$baseRel}/{$selKey}.pem"; + $pubRel = "{$baseRel}/{$selKey}.pub"; + + // 2) Idempotent: existiert das Paar schon? -> nur lesen & zurückgeben + if ($disk->exists($privRel) && $disk->exists($pubRel)) { + $privateKey = $disk->get($privRel); + $publicKeyPem = $disk->get($pubRel); + $publicKeyBase = self::extractPublicKeyBase64($publicKeyPem); + if (strlen($publicKeyBase) < 300) { + throw new \RuntimeException('DKIM: Public Key zu kurz – vermutlich Parsing-Fehler.'); + } + return [ + 'selector' => $selKey, + 'priv_path' => storage_path("app/{$privRel}"), + 'pub_path' => storage_path("app/{$pubRel}"), + 'public_pem' => $publicKeyPem, + 'private_pem' => $privateKey, + 'dns_name' => "{$selKey}._domainkey", + 'dns_txt' => "v=DKIM1; k=rsa; p={$publicKeyBase}", + 'bits' => $bits, + ]; + } + + // 3) Sonst neu generieren $disk->makeDirectory($baseRel); - // 2) Keypair via PHP-OpenSSL (kein shell_exec) $res = openssl_pkey_new([ 'private_key_type' => OPENSSL_KEYTYPE_RSA, 'private_key_bits' => $bits, @@ -42,11 +64,12 @@ class DkimService if ($details === false || empty($details['key'])) { throw new \RuntimeException('DKIM: Public Key konnte nicht gelesen werden.'); } - $publicKeyPem = $details['key']; - Log::debug('dkim.pem.first_line', ['line' => strtok($publicKeyPem, "\n")]); - Log::debug('dkim.pem.len', ['len' => strlen($publicKeyPem)]); + $publicKeyPem = $details['key']; + $publicKeyBase = self::extractPublicKeyBase64($publicKeyPem); + if (strlen($publicKeyBase) < 300) { + throw new \RuntimeException('DKIM: Public Key zu kurz – vermutlich Parsing-Fehler.'); + } - // 3) Schreiben über Storage (legt Dateien an, keine zu langen Pfade in Komponenten) if (!$disk->put($privRel, $privateKey)) { throw new \RuntimeException("DKIM: Private-Key schreiben fehlgeschlagen: {$privRel}"); } @@ -54,27 +77,18 @@ class DkimService throw new \RuntimeException("DKIM: Public-Key schreiben fehlgeschlagen: {$pubRel}"); } - - $publicKeyBase64 = self::extractPublicKeyBase64($publicKeyPem); - Log::debug('dkim.p.len', ['len' => strlen($publicKeyBase64)]); - $dnsTxt = "v=DKIM1; k=rsa; p={$publicKeyBase64}"; - - // sanity: RSA2048 liegt typ. > 300 chars - if (strlen($publicKeyBase64) < 300) { - throw new \RuntimeException('DKIM: Public Key zu kurz – vermutlich Parsing-Fehler.'); - } + // 4) Rückgabe return [ 'selector' => $selKey, 'priv_path' => storage_path("app/{$privRel}"), 'pub_path' => storage_path("app/{$pubRel}"), 'public_pem' => $publicKeyPem, 'private_pem' => $privateKey, - 'dns_name' => "{$selKey}._domainkey", // vor Domain hängen - 'dns_txt' => $dnsTxt, + 'dns_name' => "{$selKey}._domainkey", + 'dns_txt' => "v=DKIM1; k=rsa; p={$publicKeyBase}", 'bits' => $bits, ]; } - protected function safeKey($value, int $max = 64): string { if (is_object($value)) { diff --git a/config/mailpool.php b/config/mailpool.php index d79a164..0e5c1a1 100644 --- a/config/mailpool.php +++ b/config/mailpool.php @@ -13,14 +13,17 @@ return [ 'dmarc_policy' => env('MAILPOOL_DMARC_POLICY', 'none'), 'defaults' => [ - 'max_aliases' => (int) env('MAILPOOL_DEFAULT_MAX_ALIASES', 400), - 'max_mailboxes' => (int) env('MAILPOOL_DEFAULT_MAX_MAILBOXES', 10), + 'dkim_selector' => 'mwl1', + 'dkim_bits' => 2048, - 'default_quota_mb' => (int) env('MAILPOOL_DEFAULT_MAILBOX_QUOTA_MB', 3072), - 'max_quota_per_mailbox_mb' => env('MAILPOOL_DEFAULT_MAX_MAILBOX_QUOTA_MB', 3072), - 'total_quota_mb' => (int) env('MAILPOOL_DEFAULT_DOMAIN_TOTAL_MB', 10240), + 'max_aliases' => (int) env('MAILPOOL_DEFAULT_MAX_ALIASES', 400), + 'max_mailboxes' => (int) env('MAILPOOL_DEFAULT_MAX_MAILBOXES', 10), - 'rate_limit_per_hour' => env('MAILPOOL_DEFAULT_RATE_LIMIT_PER_HOUR', null), - 'rate_limit_override' => (bool) env('MAILPOOL_DEFAULT_RATE_OVERRIDE', false), + 'default_quota_mb' => (int) env('MAILPOOL_DEFAULT_MAILBOX_QUOTA_MB', 3072), + 'max_quota_per_mailbox_mb' => env('MAILPOOL_DEFAULT_MAX_MAILBOX_QUOTA_MB', 3072), + 'total_quota_mb' => (int) env('MAILPOOL_DEFAULT_DOMAIN_TOTAL_MB', 10240), + + 'rate_limit_per_hour' => env('MAILPOOL_DEFAULT_RATE_LIMIT_PER_HOUR', null), + 'rate_limit_override' => (bool) env('MAILPOOL_DEFAULT_RATE_OVERRIDE', false), ], ]; diff --git a/config/mailwolt.php b/config/mailwolt.php deleted file mode 100644 index e2b9649..0000000 --- a/config/mailwolt.php +++ /dev/null @@ -1,13 +0,0 @@ - env('BASE_DOMAIN', 'example.com'), - 'sysmail_sub' => env('SYSMAIL_SUB', 'sysmail'), - 'dkim_selector' => env('DKIM_SELECTOR', 'mwl1'), - - 'dkim' => [ - 'selector' => env('DKIM_SELECTOR', 'mwl1'), - 'bits' => (int) env('DKIM_BITS', 2048), - ], - -];