664 lines
26 KiB
PHP
664 lines
26 KiB
PHP
<?php
|
||
|
||
namespace Database\Seeders;
|
||
|
||
use App\Models\DkimKey;
|
||
use App\Models\Domain;
|
||
use App\Models\MailAlias;
|
||
use App\Models\MailAliasRecipient;
|
||
use App\Models\MailUser;
|
||
use App\Models\TlsaRecord;
|
||
use App\Services\DnsRecordService;
|
||
use App\Services\TlsaService;
|
||
use Illuminate\Database\Seeder;
|
||
use Illuminate\Foundation\Auth\User;
|
||
use Illuminate\Support\Str;
|
||
|
||
class SystemDomainSeeder extends Seeder
|
||
{
|
||
public function run(): void
|
||
{
|
||
// ---- Basis aus ENV/Config ------------------------------------------------
|
||
$platformBase = strtolower(config('mailpool.platform_zone', env('BASE_DOMAIN', 'example.com'))); // z.B. nexlab.at
|
||
if (!$platformBase || $platformBase === 'example.com') {
|
||
$this->command->warn("BASE_DOMAIN ist 'example.com' – Seeder überspringt produktive Einträge.");
|
||
return;
|
||
}
|
||
|
||
$mtaSub = strtolower(env('MTA_SUB', 'mx') ?: 'mx'); // z.B. mx
|
||
$systemSub = strtolower(config('mailpool.platform_system_zone') ?: 'sysmail'); // z.B. sysmail
|
||
|
||
$serverFqdn = strtolower("{$mtaSub}.{$platformBase}"); // mx.nexlab.at
|
||
$systemFqdn = strtolower("{$systemSub}.{$platformBase}"); // sysmail.nexlab.at
|
||
|
||
// =========================================================================
|
||
// 1) MAILSERVER-DOMAIN zuerst (mx.<BASE_DOMAIN>), is_server = true
|
||
// =========================================================================
|
||
$serverDomain = Domain::firstOrCreate(
|
||
['domain' => $serverFqdn],
|
||
[
|
||
'is_active' => true,
|
||
'is_system' => false,
|
||
'is_server' => true,
|
||
|
||
// nix anlegen unter mx.<base>
|
||
'max_mailboxes' => 0,
|
||
'max_aliases' => 0,
|
||
|
||
// Quotas praktisch sperren
|
||
'default_quota_mb' => 0,
|
||
'max_quota_per_mailbox_mb' => 0,
|
||
'total_quota_mb' => 0,
|
||
|
||
// kein Versand über diese Domain
|
||
'rate_limit_per_hour' => 0,
|
||
'rate_limit_override' => false,
|
||
]
|
||
);
|
||
|
||
$serverDomain->fill([
|
||
'is_active' => true,
|
||
'is_system' => false,
|
||
'is_server' => true,
|
||
'max_mailboxes' => 0,
|
||
'max_aliases' => 0,
|
||
'default_quota_mb' => 0,
|
||
'max_quota_per_mailbox_mb' => 0,
|
||
'total_quota_mb' => 0,
|
||
'rate_limit_per_hour' => 0,
|
||
'rate_limit_override' => false,
|
||
])->save();
|
||
|
||
Domain::where('is_server', true)
|
||
->where('id', '!=', $serverDomain->id)
|
||
->update(['is_server' => false]);
|
||
|
||
// Falls bereits vorhanden, sicherstellen, dass is_server=true bleibt
|
||
if (!$serverDomain->is_server) {
|
||
$serverDomain->is_server = true;
|
||
$serverDomain->save();
|
||
$this->command->info("Server-Host markiert: {$serverDomain->domain}");
|
||
}
|
||
|
||
// --- TLSA (3 1 1) prüfen/erzeugen (nur wenn noch nicht vorhanden) -------
|
||
$hasTlsa = TlsaRecord::where('domain_id', $serverDomain->id)
|
||
->where('host', $serverFqdn)
|
||
->where('service', '_25._tcp')
|
||
->exists();
|
||
|
||
if (!$hasTlsa) {
|
||
// TlsaService nutzt denselben Host aus ENV (mx.<base>) – passt zu serverFqdn.
|
||
// $tlsa = app(TlsaService::class)->refreshForServerDomain($serverDomain);
|
||
$tlsa = app(TlsaService::class)->refreshForMx('_25._tcp');
|
||
if ($tlsa) {
|
||
$this->command->info("TLSA erstellt: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}");
|
||
} else {
|
||
$path = "/etc/letsencrypt/live/{$serverFqdn}/fullchain.pem";
|
||
$this->command->warn("TLSA übersprungen: LE-Zertifikat (noch) nicht vorhanden unter {$path}");
|
||
}
|
||
} else {
|
||
$this->command->line("TLSA bereits vorhanden, übersprungen.");
|
||
}
|
||
|
||
// =========================================================================
|
||
// 2) SYSTEM-DOMAIN danach (sysmail.<BASE_DOMAIN>), is_system = true
|
||
// =========================================================================
|
||
$systemDomain = Domain::firstOrCreate(
|
||
['domain' => $systemFqdn],
|
||
[
|
||
'is_active' => true,
|
||
'is_system' => true,
|
||
|
||
// Limits
|
||
'max_aliases' => 20,
|
||
'max_mailboxes'=> 1,
|
||
|
||
// Quota
|
||
'default_quota_mb' => 512,
|
||
'max_quota_per_mailbox_mb' => 2048,
|
||
'total_quota_mb' => 2048,
|
||
|
||
// Rate limiting
|
||
'rate_limit_per_hour' => 600,
|
||
'rate_limit_override' => false,
|
||
]
|
||
);
|
||
|
||
$systemDomain->fill([
|
||
'is_active' => true,
|
||
'is_system' => true,
|
||
'max_aliases' => 20,
|
||
'max_mailboxes' => 1,
|
||
'default_quota_mb' => 512,
|
||
'max_quota_per_mailbox_mb' => 2048,
|
||
'total_quota_mb' => 2048,
|
||
'rate_limit_per_hour' => 600,
|
||
'rate_limit_override' => false,
|
||
])->save();
|
||
|
||
if ($systemDomain->wasRecentlyCreated) {
|
||
$this->command->line("System-Domain angelegt: {$systemDomain->domain}");
|
||
}
|
||
|
||
$noReply = MailUser::firstOrCreate(
|
||
['domain_id' => $systemDomain->id, 'localpart' => 'no-reply'],
|
||
[
|
||
'email' => 'no-reply@' . $systemDomain->domain,
|
||
'password_hash' => null,
|
||
'is_active' => true,
|
||
'is_system' => true,
|
||
'quota_mb' => 0,
|
||
]
|
||
);
|
||
|
||
$addRecipient = function (MailAlias $alias, MailUser $user) {
|
||
// sichere, vollständige Adresse bauen
|
||
$user->loadMissing('domain');
|
||
$addr = $user->localpart.'@'.$user->domain->domain;
|
||
|
||
MailAliasRecipient::create([
|
||
'alias_id' => $alias->id,
|
||
'mail_user_id' => $user->id, // Referenz
|
||
'email' => $addr, // denormalisierte, lesbare Adresse
|
||
'position' => 0,
|
||
]);
|
||
};
|
||
|
||
$seedGroup = function (string $local, MailUser $user) use ($systemDomain, $addRecipient) {
|
||
$alias = MailAlias::updateOrCreate(
|
||
['domain_id' => $systemDomain->id, 'local' => $local],
|
||
['type' => 'group', 'is_active' => true, 'is_system' => true]
|
||
);
|
||
$alias->recipients()->delete();
|
||
$addRecipient($alias, $user);
|
||
};
|
||
|
||
$seedGroup('system', $noReply);
|
||
$seedGroup('bounces', $noReply);
|
||
$seedGroup('postmaster', $noReply);
|
||
$seedGroup('abuse', $noReply);
|
||
|
||
$this->command->info("System-Domain '{$systemFqdn}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
|
||
$this->printDnsHints($systemDomain);
|
||
}
|
||
|
||
/** @return array{0:string privatePem,1:string publicTxt} */
|
||
private function generateDkimKeyPair(): array
|
||
{
|
||
$res = openssl_pkey_new([
|
||
'private_key_bits' => 2048,
|
||
'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
||
]);
|
||
openssl_pkey_export($res, $privateKeyPem);
|
||
$details = openssl_pkey_get_details($res);
|
||
$pubDer = $details['key'];
|
||
// Public PEM zu reinem Base64 für DKIM p= normalisieren
|
||
$publicTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer));
|
||
return [$privateKeyPem, $publicTxt];
|
||
}
|
||
|
||
private function printDnsHints(Domain $domain): void
|
||
{
|
||
$base = $domain->domain;
|
||
|
||
$dkim = $domain->dkimKeys()->where('is_active', true)->latest()->first();
|
||
if ($dkim) {
|
||
$this->command->line(" • DKIM TXT @ {$dkim->selector}._domainkey.{$base}");
|
||
$this->command->line(" v=DKIM1; k=rsa; p={$dkim->public_key_txt}");
|
||
}
|
||
|
||
$spf = $domain->spf()->where('is_active', true)->latest()->first();
|
||
if ($spf) {
|
||
$this->command->line(" • SPF TXT @ {$base}");
|
||
$this->command->line(" {$spf->record_txt}");
|
||
}
|
||
|
||
$dmarc = $domain->dmarc()->where('is_active', true)->latest()->first();
|
||
if ($dmarc) {
|
||
$this->command->line(" • DMARC TXT @ _dmarc.{$base}");
|
||
$this->command->line(" " . ($dmarc->record_txt ?? $dmarc->renderTxt()));
|
||
}
|
||
}
|
||
}
|
||
|
||
//namespace Database\Seeders;
|
||
//
|
||
//use App\Models\DkimKey;
|
||
//use App\Models\Domain;
|
||
//use App\Models\MailUser;
|
||
//use App\Models\TlsaRecord;
|
||
//
|
||
//// ← NEU: fürs exists()
|
||
//use App\Services\DnsRecordService;
|
||
//use App\Services\TlsaService;
|
||
//use Illuminate\Database\Seeder;
|
||
//
|
||
//class SystemDomainSeeder extends Seeder
|
||
//{
|
||
// public function run(): void
|
||
// {
|
||
// // --- Basiswerte aus Config/ENV ---
|
||
// $platformBase = config('mailpool.platform_zone', env('BASE_DOMAIN', 'example.com')); // z.B. nexlab.at
|
||
// if (!$platformBase || $platformBase === 'example.com') {
|
||
// $this->command->warn("BASE_DOMAIN ist 'example.com' – Seeder überspringt produktive Einträge.");
|
||
// return;
|
||
// }
|
||
//
|
||
// $mtaSub = env('MTA_SUB', 'mx') ?: 'mx'; // z.B. mx
|
||
// $serverFqdn = "{$mtaSub}.{$platformBase}"; // z.B. mx.nexlab.at
|
||
//
|
||
// $systemSub = config('mailpool.platform_system_zone') ?: 'sysmail';
|
||
// $systemFqdn = "{$systemSub}.{$platformBase}"; // z.B. sysmail.nexlab.at
|
||
//
|
||
// // =========================================================================
|
||
// // 1) MAILSERVER-DOMAIN zuerst (mx.<BASE_DOMAIN>), is_server = true
|
||
// // =========================================================================
|
||
// $serverDomain = Domain::firstOrCreate(
|
||
// ['domain' => $serverFqdn],
|
||
// ['is_active' => true, 'is_system' => false, 'is_server' => true]
|
||
// );
|
||
//
|
||
// if (!$serverDomain->is_server) {
|
||
// $serverDomain->is_server = true;
|
||
// $serverDomain->save();
|
||
// $this->command->info("Server-Host markiert: {$serverDomain->domain}");
|
||
// }
|
||
//
|
||
// // --- TLSA (3 1 1) prüfen/erzeugen (nur wenn LE-Zert schon da ist) -----
|
||
// $hasTlsa = TlsaRecord::where('domain_id', $serverDomain->id)
|
||
// ->where('host', $serverFqdn)
|
||
// ->where('service', '_25._tcp')
|
||
// ->exists();
|
||
//
|
||
// if (!$hasTlsa) {
|
||
// $tlsa = app(TlsaService::class)->refreshForServerDomain($serverDomain);
|
||
// if ($tlsa) {
|
||
// $this->command->info("TLSA erstellt: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}");
|
||
// } else {
|
||
// $path = "/etc/letsencrypt/live/{$serverDomain->domain}/fullchain.pem";
|
||
// $this->command->warn("TLSA übersprungen: LE-Zertifikat (noch) nicht vorhanden unter {$path}");
|
||
// }
|
||
// }
|
||
//
|
||
// // =========================================================================
|
||
// // 2) SYSTEM-DOMAIN danach (sysmail.<BASE_DOMAIN>), is_system = true
|
||
// // =========================================================================
|
||
// $systemDomain = Domain::firstOrCreate(
|
||
// ['domain' => $systemFqdn],
|
||
// ['is_active' => true, 'is_system' => true]
|
||
// );
|
||
//
|
||
// // System-Absender (no-reply) – ohne Passwort (kein Login)
|
||
// MailUser::firstOrCreate(
|
||
// ['email' => "no-reply@{$systemFqdn}"],
|
||
// [
|
||
// 'domain_id' => $systemDomain->id,
|
||
// 'localpart' => 'no-reply',
|
||
// 'password_hash' => null,
|
||
// 'is_active' => true,
|
||
// 'is_system' => true,
|
||
// 'must_change_pw' => false,
|
||
// 'quota_mb' => 0,
|
||
// ]
|
||
// );
|
||
//
|
||
// // DKIM – Key erzeugen, falls keiner aktiv existiert
|
||
// if (!$systemDomain->dkimKeys()->where('is_active', true)->exists()) {
|
||
// [$privPem, $pubTxt] = $this->generateDkimKeyPair();
|
||
// $selector = 'mwl1'; // frei wählbar, später rotieren
|
||
//
|
||
// DkimKey::create([
|
||
// 'domain_id' => $systemDomain->id,
|
||
// 'selector' => $selector,
|
||
// 'private_key_pem' => $privPem,
|
||
// 'public_key_txt' => $pubTxt,
|
||
// 'is_active' => true,
|
||
// ]);
|
||
//
|
||
// $this->command->info("DKIM angelegt: Host = {$selector}._domainkey.{$systemFqdn}");
|
||
// }
|
||
//
|
||
// $dk = $systemDomain->dkimKeys()->where('is_active', true)->latest()->first();
|
||
// $dkimTxt = $dk ? "v=DKIM1; k=rsa; p={$dk->public_key_txt}" : null;
|
||
//
|
||
// app(DnsRecordService::class)->provision(
|
||
// $systemDomain,
|
||
// dkimSelector: $dk?->selector,
|
||
// dkimTxt: $dkimTxt,
|
||
// opts: [
|
||
// 'dmarc_policy' => 'none',
|
||
// 'spf_tail' => '-all',
|
||
// // optional: 'ipv4' => $serverIp, 'ipv6' => ...
|
||
// ]
|
||
// );
|
||
//
|
||
// $this->command->info("System-Domain '{$systemFqdn}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
|
||
// $this->printDnsHints($systemDomain);
|
||
// }
|
||
//
|
||
// /** @return array{0:string privatePem,1:string publicTxt} */
|
||
// private function generateDkimKeyPair(): array
|
||
// {
|
||
// $res = openssl_pkey_new([
|
||
// 'private_key_bits' => 2048,
|
||
// 'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
||
// ]);
|
||
// openssl_pkey_export($res, $privateKeyPem);
|
||
// $details = openssl_pkey_get_details($res);
|
||
// $pubDer = $details['key'];
|
||
// $publicTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer));
|
||
// return [$privateKeyPem, $publicTxt];
|
||
// }
|
||
//
|
||
// private function printDnsHints(Domain $domain): void
|
||
// {
|
||
// $base = $domain->domain;
|
||
//
|
||
// $dkim = $domain->dkimKeys()->where('is_active', true)->latest()->first();
|
||
// if ($dkim) {
|
||
// $this->command->line(" • DKIM TXT @ {$dkim->selector}._domainkey.{$base}");
|
||
// $this->command->line(" v=DKIM1; k=rsa; p={$dkim->public_key_txt}");
|
||
// }
|
||
//
|
||
// $spf = $domain->spf()->where('is_active', true)->latest()->first();
|
||
// if ($spf) {
|
||
// $this->command->line(" • SPF TXT @ {$base}");
|
||
// $this->command->line(" {$spf->record_txt}");
|
||
// }
|
||
//
|
||
// $dmarc = $domain->dmarc()->where('is_active', true)->latest()->first();
|
||
// if ($dmarc) {
|
||
// $this->command->line(" • DMARC TXT @ _dmarc.{$base}");
|
||
// $this->command->line(" " . ($dmarc->record_txt ?? $dmarc->renderTxt()));
|
||
// }
|
||
// }
|
||
//}
|
||
|
||
|
||
//namespace Database\Seeders;
|
||
//
|
||
//use App\Models\DkimKey;
|
||
//use App\Models\Domain;
|
||
//use App\Models\MailUser;
|
||
//use App\Services\DnsRecordService;
|
||
//use App\Services\TlsaService;
|
||
//use Illuminate\Database\Seeder;
|
||
//
|
||
//class SystemDomainSeeder extends Seeder
|
||
//{
|
||
// public function run(): void
|
||
// {
|
||
// // --- Basiswerte aus Config/ENV ---
|
||
// $platformBase = config('mailpool.platform_zone', env('BASE_DOMAIN', 'example.com')); // z.B. nexlab.at
|
||
// if (!$platformBase || $platformBase === 'example.com') {
|
||
// $this->command->warn("BASE_DOMAIN ist 'example.com' – Seeder überspringt produktive Einträge.");
|
||
// return;
|
||
// }
|
||
//
|
||
// $mtaSub = env('MTA_SUB', 'mx') ?: 'mx'; // z.B. mx
|
||
// $serverFqdn = "{$mtaSub}.{$platformBase}"; // z.B. mx.nexlab.at
|
||
//
|
||
// $systemSub = config('mailpool.platform_system_zone') ?: 'sysmail'; // z.B. sysmail
|
||
// $systemFqdn = "{$systemSub}.{$platformBase}"; // z.B. sysmail.nexlab.at
|
||
//
|
||
// // =========================================================================
|
||
// // 1) MAILSERVER-DOMAIN zuerst (mx.<BASE_DOMAIN>), is_server = true
|
||
// // =========================================================================
|
||
// $serverDomain = Domain::firstOrCreate(
|
||
// ['domain' => $serverFqdn],
|
||
// ['is_active' => true, 'is_system' => false, 'is_server' => true]
|
||
// );
|
||
//
|
||
// if (!$serverDomain->is_server) {
|
||
// $serverDomain->is_server = true;
|
||
// $serverDomain->save();
|
||
// $this->command->info("Server-Host markiert: {$serverDomain->domain}");
|
||
// }
|
||
//
|
||
// // TLSA (3 1 1) nur in Laravel erzeugen/aktualisieren (falls LE-Zert bereits da ist)
|
||
// if (! $serverDomain->tlsa()->exists()) {
|
||
// $tlsa = app(\App\Services\TlsaService::class)->refreshForServerDomain($serverDomain);
|
||
// if ($tlsa) {
|
||
// $this->command->info("TLSA erstellt: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}");
|
||
// } else {
|
||
// $this->command->warn("TLSA übersprungen: LE-Zertifikat (noch) nicht vorhanden – später erneut seeden.");
|
||
// }
|
||
// } else {
|
||
// $this->command->line("TLSA bereits vorhanden, übersprungen.");
|
||
// }
|
||
//
|
||
// // =========================================================================
|
||
// // 2) SYSTEM-DOMAIN danach (sysmail.<BASE_DOMAIN>), is_system = true
|
||
// // =========================================================================
|
||
// $systemDomain = Domain::firstOrCreate(
|
||
// ['domain' => $systemFqdn],
|
||
// ['is_active' => true, 'is_system' => true]
|
||
// );
|
||
//
|
||
// // System-Absender (no-reply) – ohne Passwort (kein Login)
|
||
// MailUser::firstOrCreate(
|
||
// ['email' => "no-reply@{$systemFqdn}"],
|
||
// [
|
||
// 'domain_id' => $systemDomain->id,
|
||
// 'localpart' => 'no-reply',
|
||
// 'password_hash' => null,
|
||
// 'is_active' => true,
|
||
// 'is_system' => true,
|
||
// 'must_change_pw' => false,
|
||
// 'quota_mb' => 0,
|
||
// ]
|
||
// );
|
||
//
|
||
// // DKIM – Key erzeugen, falls keiner aktiv existiert
|
||
// if (!$systemDomain->dkimKeys()->where('is_active', true)->exists()) {
|
||
// [$privPem, $pubTxt] = $this->generateDkimKeyPair();
|
||
// $selector = 'mwl1'; // frei wählbar, später rotieren
|
||
//
|
||
// DkimKey::create([
|
||
// 'domain_id' => $systemDomain->id,
|
||
// 'selector' => $selector,
|
||
// 'private_key_pem' => $privPem,
|
||
// 'public_key_txt' => $pubTxt,
|
||
// 'is_active' => true,
|
||
// ]);
|
||
//
|
||
// $this->command->info("DKIM angelegt: Host = {$selector}._domainkey.{$systemFqdn}");
|
||
// }
|
||
//
|
||
// $dk = $systemDomain->dkimKeys()->where('is_active', true)->latest()->first();
|
||
// $dkimTxt = $dk ? "v=DKIM1; k=rsa; p={$dk->public_key_txt}" : null;
|
||
//
|
||
// app(DnsRecordService::class)->provision(
|
||
// $systemDomain,
|
||
// dkimSelector: $dk?->selector,
|
||
// dkimTxt: $dkimTxt,
|
||
// opts: [
|
||
// 'dmarc_policy' => 'none',
|
||
// 'spf_tail' => '-all',
|
||
// // optional: 'ipv4' => $serverIp, 'ipv6' => ...
|
||
// ]
|
||
// );
|
||
//
|
||
// $this->command->info("System-Domain '{$systemFqdn}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
|
||
// $this->printDnsHints($systemDomain);
|
||
// }
|
||
//
|
||
// /** @return array{0:string privatePem,1:string publicTxt} */
|
||
// private function generateDkimKeyPair(): array
|
||
// {
|
||
// $res = openssl_pkey_new([
|
||
// 'private_key_bits' => 2048,
|
||
// 'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
||
// ]);
|
||
// openssl_pkey_export($res, $privateKeyPem);
|
||
// $details = openssl_pkey_get_details($res);
|
||
// $pubDer = $details['key'];
|
||
// // Public PEM zu "p=" (reines Base64) normalisieren
|
||
// $publicTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer));
|
||
// return [$privateKeyPem, $publicTxt];
|
||
// }
|
||
//
|
||
// private function printDnsHints(Domain $domain): void
|
||
// {
|
||
// $base = $domain->domain;
|
||
//
|
||
// $dkim = $domain->dkimKeys()->where('is_active', true)->latest()->first();
|
||
// if ($dkim) {
|
||
// $this->command->line(" • DKIM TXT @ {$dkim->selector}._domainkey.{$base}");
|
||
// $this->command->line(" v=DKIM1; k=rsa; p={$dkim->public_key_txt}");
|
||
// }
|
||
//
|
||
// $spf = $domain->spf()->where('is_active', true)->latest()->first();
|
||
// if ($spf) {
|
||
// $this->command->line(" • SPF TXT @ {$base}");
|
||
// $this->command->line(" {$spf->record_txt}");
|
||
// }
|
||
//
|
||
// $dmarc = $domain->dmarc()->where('is_active', true)->latest()->first();
|
||
// if ($dmarc) {
|
||
// $this->command->line(" • DMARC TXT @ _dmarc.{$base}");
|
||
// $this->command->line(" " . ($dmarc->record_txt ?? $dmarc->renderTxt()));
|
||
// }
|
||
// }
|
||
//}
|
||
//
|
||
//----------------------------------------------------------------
|
||
|
||
//
|
||
//namespace Database\Seeders;
|
||
//
|
||
//use App\Models\DkimKey;
|
||
//use App\Models\Domain;
|
||
//use App\Models\MailUser;
|
||
//use App\Services\DnsRecordService;
|
||
//use Illuminate\Database\Seeder;
|
||
//
|
||
//class SystemDomainSeeder extends Seeder
|
||
//{
|
||
// public function run(): void
|
||
// {
|
||
// $base = config('mailpool.platform_zone', 'example.com');
|
||
// if (!$base || $base === 'example.com') {
|
||
// $this->command->warn("BASE_DOMAIN ist 'example.com' – Seeder überspringt produktive Einträge.");
|
||
// return;
|
||
// }
|
||
//
|
||
// $systemSub = config('mailpool.platform_system_zone');
|
||
// $base = "{$systemSub}.{$base}";
|
||
//
|
||
// // Domain anlegen/holen
|
||
// $domain = Domain::firstOrCreate(
|
||
// ['domain' => $base],
|
||
// ['is_active' => true, 'is_system' => true]
|
||
// );
|
||
//
|
||
// // System Absender (no-reply) – ohne Passwort (kein Login)
|
||
// MailUser::firstOrCreate(
|
||
// ['email' => "no-reply@{$base}"],
|
||
// [
|
||
// 'domain_id' => $domain->id,
|
||
// 'localpart' => 'no-reply',
|
||
// 'password_hash' => null,
|
||
// 'is_active' => true,
|
||
// 'is_system' => true,
|
||
// 'must_change_pw' => false,
|
||
// 'quota_mb' => 0,
|
||
// ]
|
||
// );
|
||
//
|
||
// // DKIM – Key erzeugen, falls keiner aktiv existiert
|
||
// if (! $domain->dkimKeys()->where('is_active', true)->exists()) {
|
||
// [$privPem, $pubTxt] = $this->generateDkimKeyPair();
|
||
// $selector = 'mwl1'; // frei wählbar, z. B. rotierend später
|
||
//
|
||
// DkimKey::create([
|
||
// 'domain_id' => $domain->id,
|
||
// 'selector' => $selector,
|
||
// 'private_key_pem'=> $privPem,
|
||
// 'public_key_txt' => $pubTxt,
|
||
// 'is_active' => true,
|
||
// ]);
|
||
//
|
||
// $this->command->info("DKIM angelegt: Host = {$selector}._domainkey.{$base}");
|
||
// }
|
||
//
|
||
// $dk = $domain->dkimKeys()->where('is_active', true)->latest()->first();
|
||
// $dkimTxt = $dk ? "v=DKIM1; k=rsa; p={$dk->public_key_txt}" : null;
|
||
//
|
||
// app(DnsRecordService::class)->provision(
|
||
// $domain,
|
||
// dkimSelector: $dk?->selector,
|
||
// dkimTxt: $dkimTxt,
|
||
// opts: [
|
||
// 'dmarc_policy' => 'none',
|
||
// 'spf_tail' => '-all',
|
||
// // optional: 'ipv4' => $serverIp, 'ipv6' => ...
|
||
// ]
|
||
// );
|
||
//
|
||
// $base = config('mailpool.platform_zone', env('BASE_DOMAIN', 'example.com')); // z.B. nexlab.at
|
||
// $mta = env('MTA_SUB', 'mx') ?: 'mx';
|
||
// $serverHostFqdn = "{$mta}.{$base}";
|
||
//
|
||
// $serverHostDomain = \App\Models\Domain::firstOrCreate(
|
||
// ['domain' => $serverHostFqdn],
|
||
// ['is_active' => true, 'is_system' => false, 'is_server' => true]
|
||
// );
|
||
//
|
||
// if (!$serverHostDomain->is_server) {
|
||
// $serverHostDomain->is_server = true;
|
||
// $serverHostDomain->save();
|
||
// $this->command->info("Server-Host markiert: {$serverHostDomain->domain}");
|
||
// }
|
||
//
|
||
// $tlsa = app(\App\Services\TlsaService::class)->refreshForServerDomain($serverHostDomain);
|
||
// if ($tlsa) {
|
||
// $this->command->info("TLSA gespeichert: _25._tcp.{$tlsa->host} 3 1 1 {$tlsa->hash}");
|
||
// } else {
|
||
// $this->command->warn("TLSA übersprungen: LE-Zertifikat (noch) nicht vorhanden – später erneut seeden.");
|
||
// }
|
||
//
|
||
// $this->command->info("System-Domain '{$base}' fertig. SPF/DMARC/DKIM & DNS-Empfehlungen eingetragen.");
|
||
// $this->printDnsHints($domain);
|
||
// }
|
||
//
|
||
// /** @return array{0:string privatePem,1:string publicTxt} */
|
||
// private function generateDkimKeyPair(): array
|
||
// {
|
||
// $res = openssl_pkey_new([
|
||
// 'private_key_bits' => 2048,
|
||
// 'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
||
// ]);
|
||
// openssl_pkey_export($res, $privateKeyPem);
|
||
// $details = openssl_pkey_get_details($res);
|
||
// // $details['key'] ist PEM, wir brauchen Base64 ohne Header/Footers
|
||
// $pubDer = $details['key'];
|
||
// // Public PEM zu "p=" Wert (reines Base64) normalisieren
|
||
// $pubTxt = trim(preg_replace('/-----(BEGIN|END) PUBLIC KEY-----|\s+/', '', $pubDer));
|
||
//
|
||
// return [$privateKeyPem, $pubTxt];
|
||
// }
|
||
//
|
||
// private function printDnsHints(Domain $domain): void
|
||
// {
|
||
// $base = $domain->domain;
|
||
// $dkim = $domain->dkimKeys()->where('is_active', true)->latest()->first();
|
||
// if ($dkim) {
|
||
// $this->command->line(" • DKIM TXT @ {$dkim->selector}._domainkey.{$base}");
|
||
// $this->command->line(" v=DKIM1; k=rsa; p={$dkim->public_key_txt}");
|
||
// }
|
||
//
|
||
// $spf = $domain->spf()->where('is_active', true)->latest()->first();
|
||
// if ($spf) {
|
||
// $this->command->line(" • SPF TXT @ {$base}");
|
||
// $this->command->line(" {$spf->record_txt}");
|
||
// }
|
||
//
|
||
// $dmarc = $domain->dmarc()->where('is_active', true)->latest()->first();
|
||
// if ($dmarc) {
|
||
// $this->command->line(" • DMARC TXT @ _dmarc.{$base}");
|
||
// $this->command->line(" " . ($dmarc->record_txt ?? $dmarc->renderTxt()));
|
||
// }
|
||
// }
|
||
//}
|