diff --git a/app/Jobs/InstallDkimKey.php b/app/Jobs/InstallDkimKey.php new file mode 100644 index 0000000..e94f550 --- /dev/null +++ b/app/Jobs/InstallDkimKey.php @@ -0,0 +1,52 @@ +domainId); + $dk = DkimKey::findOrFail($this->dkimKeyId); + + $domainName = $domain->domain; // z.B. example.com + $selector = $dk->selector; // z.B. mwl1 + + // TXT temporär für Helper speichern (optional, damit /etc/mailwolt/dns gefüllt wird) + $tmpTxt = tempnam(sys_get_temp_dir(), 'dkim_txt_'); + file_put_contents($tmpTxt, $this->dnsTxtContent); + + // Root-Helper aufrufen (sudoers hast du im Installer angelegt) + $cmd = sprintf( + 'sudo /usr/local/sbin/mailwolt-install-dkim %s %s %s %s', + escapeshellarg($domainName), + escapeshellarg($selector), + escapeshellarg($this->privPath), + escapeshellarg($tmpTxt) + ); + + exec($cmd, $out, $rc); + @unlink($tmpTxt); + + if ($rc !== 0) { + throw new \RuntimeException("mailwolt-install-dkim failed (rc={$rc})"); + } + } +} diff --git a/app/Jobs/RemoveDkimKey.php b/app/Jobs/RemoveDkimKey.php new file mode 100644 index 0000000..528d095 --- /dev/null +++ b/app/Jobs/RemoveDkimKey.php @@ -0,0 +1,36 @@ +findOrFail($this->domainId); + + $cmd = sprintf( + 'sudo /usr/local/sbin/mailwolt-remove-dkim %s %s', + escapeshellarg($domain->domain), + escapeshellarg($this->selector) + ); + + exec($cmd, $out, $rc); + if ($rc !== 0) { + throw new \RuntimeException("mailwolt-remove-dkim failed (rc={$rc})"); + } + } +} diff --git a/app/Livewire/Ui/Domain/Modal/DomainCreateModal.php b/app/Livewire/Ui/Domain/Modal/DomainCreateModal.php index 6811673..926e03d 100644 --- a/app/Livewire/Ui/Domain/Modal/DomainCreateModal.php +++ b/app/Livewire/Ui/Domain/Modal/DomainCreateModal.php @@ -3,6 +3,7 @@ namespace App\Livewire\Ui\Domain\Modal; +use App\Jobs\InstallDkimKey; use App\Models\DkimKey; use App\Models\Domain; use App\Models\Setting; @@ -271,7 +272,7 @@ class DomainCreateModal extends ModalComponent // DKIM + DNS $dkim = app(DkimService::class)->generateForDomain($domain, $this->dkim_bits, $this->dkim_selector); - DkimKey::create([ + $dk = DkimKey::create([ 'domain_id' => $domain->id, 'selector' => $dkim['selector'], 'private_key_pem' => $dkim['private_pem'], @@ -279,6 +280,13 @@ class DomainCreateModal extends ModalComponent 'is_active' => true, ]); + dispatch(new InstallDkimKey( + domainId: $domain->id, + dkimKeyId: $dk->id, + privPath: $dkim['priv_path'], + dnsTxtContent: $dkim['dns_txt'] + )); + app(DnsRecordService::class)->provision( $domain, $dkim['selector'] ?? null, diff --git a/app/Observers/DomainObserver.php b/app/Observers/DomainObserver.php new file mode 100644 index 0000000..491b77b --- /dev/null +++ b/app/Observers/DomainObserver.php @@ -0,0 +1,66 @@ +generateForDomain( + domainId: $domain, + bits: $bits, + selector: $selector + ); + + // In dkim_keys speichern + $dk = DkimKey::create([ + 'domain_id' => $domain->id, + 'selector' => $res['selector'], + 'private_key_pem' => $res['private_pem'], + 'public_key_txt' => preg_replace('/^v=DKIM1; k=rsa; p=/', '', $res['dns_txt']), + 'is_active' => true, + ]); + + // Helper-Job zum Installieren starten + InstallDkimKey::dispatch( + domainId: $domain->id, + dkimKeyId: $dk->id, + privPath: $res['priv_path'], + dnsTxtContent: $res['dns_txt'] + )->afterCommit(); + } + + /** + * Beim Löschen alle DKIM-Selector dieser Domain aus OpenDKIM entfernen. + */ + public function deleted(Domain $domain): void + { + // Falls SoftDeletes im Spiel, willst du evtl. forceDeleted spiegeln (s.u.) + foreach ($domain->dkimKeys()->get() as $dk) { + RemoveDkimKey::dispatch( + domainId: $domain->id, + selector: $dk->selector + )->afterCommit(); + } + } + + public function forceDeleted(Domain $domain): void + { + $this->deleted($domain); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 9d0b3bb..73832b3 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,8 @@ namespace App\Providers; +use App\Models\Domain; +use App\Observers\DomainObserver; use App\Support\SettingsRepository; use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; @@ -21,6 +23,9 @@ class AppServiceProvider extends ServiceProvider */ public function boot(\App\Support\SettingsRepository $settings): void { + + Domain::observe(DomainObserver::class); + try { $S = app(\App\Support\SettingsRepository::class); diff --git a/app/Services/DkimService.php b/app/Services/DkimService.php index 5c529a0..5ff3848 100644 --- a/app/Services/DkimService.php +++ b/app/Services/DkimService.php @@ -54,9 +54,6 @@ class DkimService throw new \RuntimeException("DKIM: Public-Key schreiben fehlgeschlagen: {$pubRel}"); } - // 4) DNS-Record bauen -// $p = preg_replace('/-----BEGIN PUBLIC KEY-----|-----END PUBLIC KEY-----|\s+/', '', $publicKeyPem); -// $dnsTxt = "v=DKIM1; k=rsa; p={$p}"; $publicKeyBase64 = self::extractPublicKeyBase64($publicKeyPem); Log::debug('dkim.p.len', ['len' => strlen($publicKeyBase64)]); diff --git a/config/mailwolt.php b/config/mailwolt.php index a946d15..e2b9649 100644 --- a/config/mailwolt.php +++ b/config/mailwolt.php @@ -4,4 +4,10 @@ return [ 'base_domain' => 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), + ], + ];