Domain Create Modal anpassen Fehler auf Null
parent
890184d985
commit
d6007589ef
|
|
@ -4,8 +4,8 @@ namespace App\Livewire\Ui\Domain;
|
||||||
|
|
||||||
use App\Models\Domain;
|
use App\Models\Domain;
|
||||||
use App\Services\DkimService;
|
use App\Services\DkimService;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
use Illuminate\View\View;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class DkimStatus extends Component
|
class DkimStatus extends Component
|
||||||
|
|
@ -17,7 +17,7 @@ class DkimStatus extends Component
|
||||||
{
|
{
|
||||||
$this->domain = $domain;
|
$this->domain = $domain;
|
||||||
|
|
||||||
// aktiven Selector aus DB, sonst Default aus Config
|
// aktiven Selector aus DB; sonst Default aus Config
|
||||||
$this->selector = $selector
|
$this->selector = $selector
|
||||||
?: optional(
|
?: optional(
|
||||||
$domain->dkimKeys()->where('is_active', true)->latest()->first()
|
$domain->dkimKeys()->where('is_active', true)->latest()->first()
|
||||||
|
|
@ -25,27 +25,38 @@ class DkimStatus extends Component
|
||||||
?: (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
?: (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Prüft nur lokale Voraussetzungen (Keyfile + KeyTable + SigningTable) */
|
/**
|
||||||
|
* Prüft NUR lokal:
|
||||||
|
* - Keyfile: /etc/opendkim/keys/<domain>/<selector>.private
|
||||||
|
* - KeyTable & SigningTable enthalten passende Zeilen
|
||||||
|
*/
|
||||||
protected function isDkimReady(string $domain, string $selector): bool
|
protected function isDkimReady(string $domain, string $selector): bool
|
||||||
{
|
{
|
||||||
$d = preg_quote($domain, '/');
|
$d = preg_quote($domain, '/');
|
||||||
$s = preg_quote($selector, '/');
|
$s = preg_quote($selector, '/');
|
||||||
|
|
||||||
// 1) Key-Datei unter /etc/opendkim/keys/<domain>/<selector>.private
|
// 1) Key-Datei vorhanden & lesbar
|
||||||
$keyFile = "/etc/opendkim/keys/{$domain}/{$selector}.private";
|
$keyFile = "/etc/opendkim/keys/{$domain}/{$selector}.private";
|
||||||
$hasFile = is_readable($keyFile) && filesize($keyFile) > 0;
|
$hasFile = is_readable($keyFile) && (filesize($keyFile) > 0);
|
||||||
|
|
||||||
// 2) Einträge in KeyTable + SigningTable
|
// 2) Tabellen-Inhalte lesen (leer wenn Datei fehlt)
|
||||||
$keyTab = is_readable('/etc/opendkim/KeyTable') ? (string) @file_get_contents('/etc/opendkim/KeyTable') : '';
|
$keyTab = is_readable('/etc/opendkim/KeyTable')
|
||||||
$signTab = is_readable('/etc/opendkim/SigningTable') ? (string) @file_get_contents('/etc/opendkim/SigningTable') : '';
|
? (string) @file_get_contents('/etc/opendkim/KeyTable')
|
||||||
|
: '';
|
||||||
|
$signTab = is_readable('/etc/opendkim/SigningTable')
|
||||||
|
? (string) @file_get_contents('/etc/opendkim/SigningTable')
|
||||||
|
: '';
|
||||||
|
|
||||||
// Beispiele:
|
// Beispiel-Zeilen:
|
||||||
// KeyTable: mwl1._domainkey.sysmail.toastra.com sysmail.toastra.com:mwl1:/etc/opendkim/keys/sysmail.toastra.com/mwl1.private
|
// KeyTable: mwl1._domainkey.sysmail.toastra.com sysmail.toastra.com:mwl1:/etc/opendkim/keys/sysmail.toastra.com/mwl1.private
|
||||||
// SigningTable:*@sysmail.toastra.com mwl1._domainkey.sysmail.toastra.com
|
// SigningTable:*@sysmail.toastra.com mwl1._domainkey.sysmail.toastra.com
|
||||||
$inKey = (bool) preg_match(
|
|
||||||
|
// Robust gegen Mehrfach-Spaces/Tabs:
|
||||||
|
$inKey = (bool) preg_match(
|
||||||
"/^{$s}\._domainkey\.{$d}\s+{$d}:{$s}:/m",
|
"/^{$s}\._domainkey\.{$d}\s+{$d}:{$s}:/m",
|
||||||
$keyTab
|
$keyTab
|
||||||
);
|
);
|
||||||
|
|
||||||
$inSign = (bool) preg_match(
|
$inSign = (bool) preg_match(
|
||||||
"/^\*\@{$d}\s+{$s}\._domainkey\.{$d}\s*$/m",
|
"/^\*\@{$d}\s+{$s}\._domainkey\.{$d}\s*$/m",
|
||||||
$signTab
|
$signTab
|
||||||
|
|
@ -54,36 +65,42 @@ class DkimStatus extends Component
|
||||||
return $hasFile && $inKey && $inSign;
|
return $hasFile && $inKey && $inSign;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Reparieren/Neu erzeugen */
|
/** Button: (Re)generieren via Service (der ruft sudo-Helper auf) */
|
||||||
public function regenerate(?string $selector = null): void
|
public function regenerate(?string $selector = null): void
|
||||||
{
|
{
|
||||||
$selector = $selector ?: ($this->selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1'));
|
$selector = $selector
|
||||||
|
?: ($this->selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1'));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/** @var DkimService $svc */
|
/** @var DkimService $svc */
|
||||||
$svc = app(DkimService::class);
|
$svc = app(DkimService::class);
|
||||||
|
|
||||||
// erzeugt/aktualisiert Keys in storage, pflegt DB, ruft Helper auf (falls vorhanden)
|
// erzeugt/aktualisiert Keys in storage, pflegt DB,
|
||||||
|
// ruft /usr/local/sbin/mailwolt-install-dkim via sudo -n auf,
|
||||||
|
// lädt opendkim neu (im Service)
|
||||||
$svc->generateForDomain($this->domain, 2048, $selector);
|
$svc->generateForDomain($this->domain, 2048, $selector);
|
||||||
|
|
||||||
// OpenDKIM neu laden (idempotent)
|
|
||||||
Process::run(['systemctl', 'reload', 'opendkim']);
|
|
||||||
|
|
||||||
// Status neu prüfen
|
// Status neu prüfen
|
||||||
$ok = $this->isDkimReady($this->domain->domain, $selector);
|
$ok = $this->isDkimReady($this->domain->domain, $selector);
|
||||||
$this->dispatch('toast', type: $ok ? 'success' : 'warning',
|
|
||||||
message: $ok ? 'DKIM ist aktiv.' : 'DKIM generiert – OpenDKIM prüfen.');
|
$this->dispatch('toast',
|
||||||
|
type: $ok ? 'success' : 'warning',
|
||||||
|
message: $ok ? 'DKIM ist aktiv.' : 'DKIM generiert – OpenDKIM prüfen.'
|
||||||
|
);
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->dispatch('toast', type: 'error', message: 'DKIM Fehler: '.$e->getMessage());
|
$this->dispatch('toast', type: 'error', message: 'DKIM Fehler: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->selector = $selector; // für Folge-Checks
|
// aktuellen Selector merken (falls der Fallback zuvor anders war)
|
||||||
|
$this->selector = $selector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
{
|
{
|
||||||
$dkimOk = $this->isDkimReady($this->domain->domain, $this->selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1'));
|
$sel = $this->selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
||||||
|
$dkimOk = $this->isDkimReady($this->domain->domain, $sel);
|
||||||
|
|
||||||
return view('livewire.ui.domain.dkim-status', compact('dkimOk'));
|
return view('livewire.ui.domain.dkim-status', compact('dkimOk'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,253 +13,6 @@ use RuntimeException;
|
||||||
class DkimService
|
class DkimService
|
||||||
{
|
{
|
||||||
/** Erzeugt Keypair & gibt den TXT-Record (ohne Host) zurück. */
|
/** Erzeugt Keypair & gibt den TXT-Record (ohne Host) zurück. */
|
||||||
// public function generateForDomain(Domain $domain, int $bits = 2048, string $selector = null): array
|
|
||||||
// {
|
|
||||||
// // 1) Selector zentral aus der Config (Fallback 'mwl1')
|
|
||||||
// $selector = $selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
|
||||||
//
|
|
||||||
// $dirKey = $this->safeKey($domain);
|
|
||||||
// $selKey = $this->safeKey($selector, 32);
|
|
||||||
//
|
|
||||||
// $disk = Storage::disk('local');
|
|
||||||
// $baseRel = "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);
|
|
||||||
//
|
|
||||||
// $res = openssl_pkey_new([
|
|
||||||
// 'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
|
||||||
// 'private_key_bits' => $bits,
|
|
||||||
// ]);
|
|
||||||
// if ($res === false) {
|
|
||||||
// throw new \RuntimeException('DKIM: openssl_pkey_new() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// $privateKey = '';
|
|
||||||
// if (!openssl_pkey_export($res, $privateKey)) {
|
|
||||||
// throw new \RuntimeException('DKIM: openssl_pkey_export() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// $details = openssl_pkey_get_details($res);
|
|
||||||
// if ($details === false || empty($details['key'])) {
|
|
||||||
// throw new \RuntimeException('DKIM: Public Key konnte nicht gelesen werden.');
|
|
||||||
// }
|
|
||||||
// $publicKeyPem = $details['key'];
|
|
||||||
// $publicKeyBase = self::extractPublicKeyBase64($publicKeyPem);
|
|
||||||
// if (strlen($publicKeyBase) < 300) {
|
|
||||||
// throw new \RuntimeException('DKIM: Public Key zu kurz – vermutlich Parsing-Fehler.');
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (!$disk->put($privRel, $privateKey)) {
|
|
||||||
// throw new \RuntimeException("DKIM: Private-Key schreiben fehlgeschlagen: {$privRel}");
|
|
||||||
// }
|
|
||||||
// if (!$disk->put($pubRel, $publicKeyPem)) {
|
|
||||||
// throw new \RuntimeException("DKIM: Public-Key schreiben fehlgeschlagen: {$pubRel}");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 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",
|
|
||||||
// 'dns_txt' => "v=DKIM1; k=rsa; p={$publicKeyBase}",
|
|
||||||
// 'bits' => $bits,
|
|
||||||
// ];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public function generateForDomain(Domain $domain, int $bits = 2048, string $selector = null): array
|
|
||||||
// {
|
|
||||||
// // 1) Selector zentral aus der Config (Fallback 'mwl1')
|
|
||||||
// $selector = $selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
|
||||||
//
|
|
||||||
// $dirKey = $this->safeKey($domain);
|
|
||||||
// $selKey = $this->safeKey($selector, 32);
|
|
||||||
//
|
|
||||||
// $disk = Storage::disk('local');
|
|
||||||
// $baseRel = "dkim/{$dirKey}";
|
|
||||||
// $privRel = "{$baseRel}/{$selKey}.pem";
|
|
||||||
// $pubRel = "{$baseRel}/{$selKey}.pub";
|
|
||||||
//
|
|
||||||
// $privAbs = method_exists($disk, 'path')
|
|
||||||
// ? $disk->path($privRel) // -> /var/www/mailwolt/storage/app/private/dkim/<id>/<selector>.pem
|
|
||||||
// : storage_path('app/private/'.$privRel); // Fallback falls 'path' nicht existiert
|
|
||||||
//
|
|
||||||
// $pubAbs = method_exists($disk, 'path')
|
|
||||||
// ? $disk->path($pubRel)
|
|
||||||
// : storage_path('app/private/'.$pubRel);
|
|
||||||
//
|
|
||||||
// // 2) Idempotent: existiert das Paar schon?
|
|
||||||
// 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' => $privAbs,
|
|
||||||
// 'pub_path' => $pubAbs,
|
|
||||||
// 'public_pem' => $publicKeyPem,
|
|
||||||
// 'private_pem' => $privateKey,
|
|
||||||
// 'dns_name' => "{$selKey}._domainkey",
|
|
||||||
// 'dns_txt' => "v=DKIM1; k=rsa; p={$publicKeyBase}",
|
|
||||||
// 'bits' => $bits,
|
|
||||||
// ];
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 3) Neu generieren
|
|
||||||
// $disk->makeDirectory($baseRel);
|
|
||||||
//
|
|
||||||
// $res = openssl_pkey_new([
|
|
||||||
// 'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
|
||||||
// 'private_key_bits' => $bits,
|
|
||||||
// ]);
|
|
||||||
// if ($res === false) {
|
|
||||||
// throw new \RuntimeException('DKIM: openssl_pkey_new() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// $privateKey = '';
|
|
||||||
// if (!openssl_pkey_export($res, $privateKey)) {
|
|
||||||
// throw new \RuntimeException('DKIM: openssl_pkey_export() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// $details = openssl_pkey_get_details($res);
|
|
||||||
// if ($details === false || empty($details['key'])) {
|
|
||||||
// throw new \RuntimeException('DKIM: Public Key konnte nicht gelesen werden.');
|
|
||||||
// }
|
|
||||||
// $publicKeyPem = $details['key'];
|
|
||||||
// $publicKeyBase = self::extractPublicKeyBase64($publicKeyPem);
|
|
||||||
// if (strlen($publicKeyBase) < 300) {
|
|
||||||
// throw new \RuntimeException('DKIM: Public Key zu kurz – vermutlich Parsing-Fehler.');
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (!$disk->put($privRel, $privateKey)) {
|
|
||||||
// throw new \RuntimeException("DKIM: Private-Key schreiben fehlgeschlagen: {$privRel}");
|
|
||||||
// }
|
|
||||||
// if (!$disk->put($pubRel, $publicKeyPem)) {
|
|
||||||
// throw new \RuntimeException("DKIM: Public-Key schreiben fehlgeschlagen: {$pubRel}");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return [
|
|
||||||
// 'selector' => $selKey,
|
|
||||||
// 'priv_path' => $privAbs,
|
|
||||||
// 'pub_path' => $pubAbs,
|
|
||||||
// 'public_pem' => $publicKeyPem,
|
|
||||||
// 'private_pem' => $privateKey,
|
|
||||||
// 'dns_name' => "{$selKey}._domainkey",
|
|
||||||
// 'dns_txt' => "v=DKIM1; k=rsa; p={$publicKeyBase}",
|
|
||||||
// 'bits' => $bits,
|
|
||||||
// ];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public function generateForDomain(Domain $domain, int $bits = 2048, string $selector = null): array
|
|
||||||
// {
|
|
||||||
// // 1) Selector (Fallback mwl1)
|
|
||||||
// $selector = $selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
|
||||||
//
|
|
||||||
// $dirKey = $this->safeKey($domain);
|
|
||||||
// $selKey = $this->safeKey($selector, 32);
|
|
||||||
//
|
|
||||||
// $disk = Storage::disk('local'); // root: /var/www/mailwolt/storage/app/private
|
|
||||||
// $baseRel = "dkim/{$dirKey}";
|
|
||||||
// $privRel = "{$baseRel}/{$selKey}.pem";
|
|
||||||
// $pubRel = "{$baseRel}/{$selKey}.pub";
|
|
||||||
//
|
|
||||||
// // Absolute Pfade (robust gegen geändertes Disk-Root)
|
|
||||||
// $privAbs = method_exists($disk, 'path') ? $disk->path($privRel) : storage_path('app/private/'.$privRel);
|
|
||||||
// $pubAbs = method_exists($disk, 'path') ? $disk->path($pubRel) : storage_path('app/private/'.$pubRel);
|
|
||||||
//
|
|
||||||
// // 2) Idempotent: existiert das Paar schon?
|
|
||||||
// if ($disk->exists($privRel) && $disk->exists($pubRel)) {
|
|
||||||
// $privateKey = $disk->get($privRel); // ← Inhalte laden, nicht Pfade!
|
|
||||||
// $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' => $privAbs,
|
|
||||||
// 'pub_path' => $pubAbs,
|
|
||||||
// 'public_pem' => $publicKeyPem,
|
|
||||||
// 'private_pem' => $privateKey,
|
|
||||||
// 'dns_name' => "{$selKey}._domainkey",
|
|
||||||
// 'dns_txt' => "v=DKIM1; k=rsa; p={$publicKeyBase}",
|
|
||||||
// 'bits' => $bits,
|
|
||||||
// ];
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 3) Neu generieren
|
|
||||||
// $disk->makeDirectory($baseRel);
|
|
||||||
//
|
|
||||||
// $res = openssl_pkey_new([
|
|
||||||
// 'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
|
||||||
// 'private_key_bits' => $bits,
|
|
||||||
// ]);
|
|
||||||
// if ($res === false) {
|
|
||||||
// throw new \RuntimeException('DKIM: openssl_pkey_new() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// $privateKey = '';
|
|
||||||
// if (!openssl_pkey_export($res, $privateKey)) {
|
|
||||||
// throw new \RuntimeException('DKIM: openssl_pkey_export() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// $details = openssl_pkey_get_details($res);
|
|
||||||
// if ($details === false || empty($details['key'])) {
|
|
||||||
// throw new \RuntimeException('DKIM: Public Key konnte nicht gelesen werden.');
|
|
||||||
// }
|
|
||||||
// $publicKeyPem = $details['key'];
|
|
||||||
// $publicKeyBase = self::extractPublicKeyBase64($publicKeyPem);
|
|
||||||
// if (strlen($publicKeyBase) < 300) {
|
|
||||||
// throw new \RuntimeException('DKIM: Public Key zu kurz – vermutlich Parsing-Fehler.');
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (!$disk->put($privRel, $privateKey)) {
|
|
||||||
// throw new \RuntimeException("DKIM: Private-Key schreiben fehlgeschlagen: {$privRel}");
|
|
||||||
// }
|
|
||||||
// if (!$disk->put($pubRel, $publicKeyPem)) {
|
|
||||||
// throw new \RuntimeException("DKIM: Public-Key schreiben fehlgeschlagen: {$pubRel}");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return [
|
|
||||||
// 'selector' => $selKey,
|
|
||||||
// 'priv_path' => $privAbs,
|
|
||||||
// 'pub_path' => $pubAbs,
|
|
||||||
// 'public_pem' => $publicKeyPem,
|
|
||||||
// 'private_pem' => $privateKey,
|
|
||||||
// 'dns_name' => "{$selKey}._domainkey",
|
|
||||||
// 'dns_txt' => "v=DKIM1; k=rsa; p={$publicKeyBase}",
|
|
||||||
// 'bits' => $bits,
|
|
||||||
// ];
|
|
||||||
// }
|
|
||||||
|
|
||||||
public function generateForDomain(Domain $domain, int $bits = 2048, string $selector = null): array
|
public function generateForDomain(Domain $domain, int $bits = 2048, string $selector = null): array
|
||||||
{
|
{
|
||||||
$selector = $selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
$selector = $selector ?: (string) config('mailpool.defaults.dkim_selector', 'mwl1');
|
||||||
|
|
@ -309,30 +62,30 @@ class DkimService
|
||||||
'private_key_bits' => $bits,
|
'private_key_bits' => $bits,
|
||||||
]);
|
]);
|
||||||
if ($res === false) {
|
if ($res === false) {
|
||||||
throw new \RuntimeException('DKIM: openssl_pkey_new() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
throw new RuntimeException('DKIM: openssl_pkey_new() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$privatePem = '';
|
$privatePem = '';
|
||||||
if (!openssl_pkey_export($res, $privatePem)) {
|
if (!openssl_pkey_export($res, $privatePem)) {
|
||||||
throw new \RuntimeException('DKIM: openssl_pkey_export() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
throw new RuntimeException('DKIM: openssl_pkey_export() fehlgeschlagen: ' . (openssl_error_string() ?: 'unbekannt'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = openssl_pkey_get_details($res);
|
$details = openssl_pkey_get_details($res);
|
||||||
if ($details === false || empty($details['key'])) {
|
if ($details === false || empty($details['key'])) {
|
||||||
throw new \RuntimeException('DKIM: Public Key konnte nicht gelesen werden.');
|
throw new RuntimeException('DKIM: Public Key konnte nicht gelesen werden.');
|
||||||
}
|
}
|
||||||
$publicPem = $details['key'];
|
$publicPem = $details['key'];
|
||||||
$publicBase64 = trim(preg_replace('/-----(BEGIN|END)\s+PUBLIC KEY-----|\s+/', '', $publicPem));
|
$publicBase64 = trim(preg_replace('/-----(BEGIN|END)\s+PUBLIC KEY-----|\s+/', '', $publicPem));
|
||||||
if (strlen($publicBase64) < 300) {
|
if (strlen($publicBase64) < 300) {
|
||||||
throw new \RuntimeException('DKIM: Public Key zu kurz – vermutlich Parsing-Fehler.');
|
throw new RuntimeException('DKIM: Public Key zu kurz – vermutlich Parsing-Fehler.');
|
||||||
}
|
}
|
||||||
$dnsTxt = "v=DKIM1; k=rsa; p={$publicBase64}";
|
$dnsTxt = "v=DKIM1; k=rsa; p={$publicBase64}";
|
||||||
|
|
||||||
// Dateien schreiben (App + OpenDKIM)
|
// Dateien schreiben (App + OpenDKIM)
|
||||||
if (!$disk->put($privPemRel, $privatePem)) throw new \RuntimeException("DKIM: Private-Key schreiben fehlgeschlagen: {$privPemRel}");
|
if (!$disk->put($privPemRel, $privatePem)) throw new RuntimeException("DKIM: Private-Key schreiben fehlgeschlagen: {$privPemRel}");
|
||||||
if (!$disk->put($pubPemRel, $publicPem)) throw new \RuntimeException("DKIM: Public-Key schreiben fehlgeschlagen: {$pubPemRel}");
|
if (!$disk->put($pubPemRel, $publicPem)) throw new RuntimeException("DKIM: Public-Key schreiben fehlgeschlagen: {$pubPemRel}");
|
||||||
if (!$disk->put($privOKRel, $privatePem)) throw new \RuntimeException("DKIM: OpenDKIM-Key schreiben fehlgeschlagen: {$privOKRel}");
|
if (!$disk->put($privOKRel, $privatePem)) throw new RuntimeException("DKIM: OpenDKIM-Key schreiben fehlgeschlagen: {$privOKRel}");
|
||||||
if (!$disk->put($dnsTxtRel, $dnsTxt)) throw new \RuntimeException("DKIM: DNS-TXT schreiben fehlgeschlagen: {$dnsTxtRel}");
|
if (!$disk->put($dnsTxtRel, $dnsTxt)) throw new RuntimeException("DKIM: DNS-TXT schreiben fehlgeschlagen: {$dnsTxtRel}");
|
||||||
|
|
||||||
// Dateirechte (best effort)
|
// Dateirechte (best effort)
|
||||||
@chmod($privPemAbs, 0600);
|
@chmod($privPemAbs, 0600);
|
||||||
|
|
@ -351,21 +104,33 @@ class DkimService
|
||||||
);
|
);
|
||||||
|
|
||||||
// OpenDKIM einhängen (wenn Helper existiert)
|
// OpenDKIM einhängen (wenn Helper existiert)
|
||||||
if (is_executable('/usr/local/sbin/mailwolt-install-dkim')) {
|
$helper = '/usr/local/sbin/mailwolt-install-dkim';
|
||||||
$res = Process::run([
|
|
||||||
'/usr/local/sbin/mailwolt-install-dkim',
|
if (is_executable($helper)) {
|
||||||
|
$cmd = [
|
||||||
|
'sudo','-n', $helper,
|
||||||
$domain->domain,
|
$domain->domain,
|
||||||
$selKey,
|
$selKey,
|
||||||
$privOKAbs, // <-- .private (genau das will der Helper)
|
$privOKAbs, // …/storage/app/private/dkim/<dir>/<selector>.private
|
||||||
$dnsTxtAbs // optional: TXT-Datei, falls dein Helper das nutzt
|
$dnsTxtAbs // …/storage/app/private/dkim/<dir>/<selector>.txt
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
$res = Process::timeout(30)->run($cmd);
|
||||||
|
|
||||||
if ($res->failed()) {
|
if ($res->failed()) {
|
||||||
throw new \RuntimeException("OpenDKIM-Install fehlgeschlagen: {$res->error()}");
|
Log::error('DKIM install failed', [
|
||||||
|
'cmd' => implode(' ', $cmd),
|
||||||
|
'exit' => $res->exitCode(),
|
||||||
|
'out' => $res->output(),
|
||||||
|
'err' => $res->errorOutput(),
|
||||||
|
]);
|
||||||
|
throw new RuntimeException(
|
||||||
|
'OpenDKIM-Install fehlgeschlagen: '.$res->errorOutput()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenDKIM neu laden
|
// OpenDKIM neu laden (falls der Helper das nicht selbst tut)
|
||||||
Process::run(['systemctl', 'reload', 'opendkim']);
|
Process::run(['sudo','-n','systemctl','reload','opendkim']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
<div class="flex items-center gap-3 text-xs">
|
<div class="flex items-center gap-3 text-xs">
|
||||||
<div class="inline-flex items-center gap-2 px-1.5 py-0.5 rounded-full
|
<div class="inline-flex items-center gap-2 px-1.5 py-0.5 rounded-full
|
||||||
{{ $dkimOk ? 'bg-green-500/10 text-green-500 ring-1 ring-green-500/30' : 'bg-red-500/10 text-red-500 ring-1 ring-red-500/30' }}">
|
{{ $dkimOk ? 'bg-green-500/10 text-green-500 ring-1 ring-green-500/30' : 'bg-red-500/10 text-red-500 ring-1 ring-red-500/30' }}">
|
||||||
<i class="ph-bold {{ $dkimOk ? 'ph-shield-check' : 'ph-shield-warning' }} text-base"></i>
|
<i class="ph-bold {{ $dkimOk ? 'ph-shield-check' : 'ph-shield-warning' }} text-base"></i>
|
||||||
<span class="font-xs">{{ $dkimOk ? 'DKIM aktiv' : 'DKIM fehlt' }}</span>
|
<span>{{ $dkimOk ? 'DKIM aktiv' : 'DKIM fehlt' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if(!$dkimOk)
|
@if(!$dkimOk)
|
||||||
<button wire:click="regenerate"
|
<button wire:click="regenerate"
|
||||||
wire:loading.attr="disabled"
|
wire:loading.attr="disabled"
|
||||||
class="inline-flex items-center gap-2 rounded-lg px-3 py-1.5 text-xs font-medium
|
class="inline-flex items-center gap-2 rounded-lg px-3 py-1.5 text-xs font-medium
|
||||||
border border-slate-600/40 bg-slate-800/40 hover:bg-slate-800
|
border border-slate-600/40 bg-slate-800/40 hover:bg-slate-800
|
||||||
disabled:opacity-50">
|
disabled:opacity-50">
|
||||||
<i class="ph-bold ph-arrow-clockwise text-base"
|
<i class="ph-bold ph-arrow-clockwise text-base"
|
||||||
wire:loading.class="animate-spin"></i>
|
wire:loading.class="animate-spin"></i>
|
||||||
<span wire:loading.remove>DKIM reparieren</span>
|
<span wire:loading.remove>DKIM reparieren</span>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue