135 lines
5.3 KiB
PHP
135 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Ui\Security;
|
|
|
|
use App\Models\Setting;
|
|
use Illuminate\Support\Facades\Process;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\Title;
|
|
use Livewire\Component;
|
|
|
|
#[Layout('layouts.dvx')]
|
|
#[Title('TLS-Ciphers · Mailwolt')]
|
|
class TlsCiphersForm extends Component
|
|
{
|
|
public string $preset = 'intermediate';
|
|
|
|
public string $postfix_protocols = '!SSLv2, !SSLv3, !TLSv1, !TLSv1.1';
|
|
public string $postfix_ciphers = 'medium';
|
|
|
|
public string $dovecot_min_proto = 'TLSv1.2';
|
|
public string $dovecot_ciphers = 'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+AES256:!aNULL:!MD5:!DSS';
|
|
|
|
private const PRESETS = [
|
|
'modern' => [
|
|
'postfix_protocols' => '!SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2',
|
|
'postfix_ciphers' => 'high',
|
|
'dovecot_min_proto' => 'TLSv1.3',
|
|
'dovecot_ciphers' => 'ECDH+AESGCM:ECDH+CHACHA20:!aNULL:!MD5',
|
|
],
|
|
'intermediate' => [
|
|
'postfix_protocols' => '!SSLv2, !SSLv3, !TLSv1, !TLSv1.1',
|
|
'postfix_ciphers' => 'medium',
|
|
'dovecot_min_proto' => 'TLSv1.2',
|
|
'dovecot_ciphers' => 'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+AES256:!aNULL:!MD5:!DSS',
|
|
],
|
|
'old' => [
|
|
'postfix_protocols' => '!SSLv2, !SSLv3',
|
|
'postfix_ciphers' => 'low',
|
|
'dovecot_min_proto' => 'TLSv1',
|
|
'dovecot_ciphers' => 'HIGH:MEDIUM:!aNULL:!MD5',
|
|
],
|
|
];
|
|
|
|
public function mount(): void
|
|
{
|
|
$this->preset = Setting::get('tls.preset', 'intermediate');
|
|
$this->postfix_protocols = Setting::get('tls.postfix_protocols', self::PRESETS['intermediate']['postfix_protocols']);
|
|
$this->postfix_ciphers = Setting::get('tls.postfix_ciphers', self::PRESETS['intermediate']['postfix_ciphers']);
|
|
$this->dovecot_min_proto = Setting::get('tls.dovecot_min_proto', self::PRESETS['intermediate']['dovecot_min_proto']);
|
|
$this->dovecot_ciphers = Setting::get('tls.dovecot_ciphers', self::PRESETS['intermediate']['dovecot_ciphers']);
|
|
}
|
|
|
|
public function applyPreset(string $preset): void
|
|
{
|
|
if (!isset(self::PRESETS[$preset])) return;
|
|
$p = self::PRESETS[$preset];
|
|
$this->preset = $preset;
|
|
$this->postfix_protocols = $p['postfix_protocols'];
|
|
$this->postfix_ciphers = $p['postfix_ciphers'];
|
|
$this->dovecot_min_proto = $p['dovecot_min_proto'];
|
|
$this->dovecot_ciphers = $p['dovecot_ciphers'];
|
|
}
|
|
|
|
public function save(): void
|
|
{
|
|
$this->validate([
|
|
'postfix_protocols' => 'required|string|max:200',
|
|
'postfix_ciphers' => 'required|string|max:500',
|
|
'dovecot_min_proto' => 'required|string|max:50',
|
|
'dovecot_ciphers' => 'required|string|max:500',
|
|
]);
|
|
|
|
Setting::setMany([
|
|
'tls.preset' => $this->preset,
|
|
'tls.postfix_protocols' => $this->postfix_protocols,
|
|
'tls.postfix_ciphers' => $this->postfix_ciphers,
|
|
'tls.dovecot_min_proto' => $this->dovecot_min_proto,
|
|
'tls.dovecot_ciphers' => $this->dovecot_ciphers,
|
|
]);
|
|
|
|
try {
|
|
$this->writePostfixConfig();
|
|
$this->writeDovecotConfig();
|
|
|
|
Process::run(['sudo', '-n', '/usr/bin/systemctl', 'reload', 'postfix']);
|
|
Process::run(['sudo', '-n', '/usr/bin/systemctl', 'reload', 'dovecot']);
|
|
|
|
$this->dispatch('toast', type: 'done', badge: 'TLS',
|
|
title: 'TLS-Konfiguration übernommen',
|
|
text: 'Postfix und Dovecot wurden neu geladen.', duration: 5000);
|
|
} catch (\Throwable $e) {
|
|
$this->dispatch('toast', type: 'error', badge: 'TLS',
|
|
title: 'Fehler', text: $e->getMessage(), duration: 0);
|
|
}
|
|
}
|
|
|
|
private function writePostfixConfig(): void
|
|
{
|
|
$target = '/etc/postfix/mailwolt-tls.cf';
|
|
$content = "smtpd_tls_protocols = {$this->postfix_protocols}\n"
|
|
. "smtp_tls_protocols = {$this->postfix_protocols}\n"
|
|
. "smtpd_tls_ciphers = {$this->postfix_ciphers}\n"
|
|
. "smtp_tls_ciphers = {$this->postfix_ciphers}\n";
|
|
$this->tee($target, $content);
|
|
}
|
|
|
|
private function writeDovecotConfig(): void
|
|
{
|
|
$target = '/etc/dovecot/conf.d/99-mailwolt-tls.conf';
|
|
$content = "ssl_min_protocol = {$this->dovecot_min_proto}\n"
|
|
. "ssl_cipher_list = {$this->dovecot_ciphers}\n";
|
|
$this->tee($target, $content);
|
|
}
|
|
|
|
private function tee(string $target, string $content): void
|
|
{
|
|
$proc = proc_open(
|
|
sprintf('sudo -n /usr/bin/tee %s >/dev/null', escapeshellarg($target)),
|
|
[0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']],
|
|
$pipes
|
|
);
|
|
if (!is_resource($proc)) throw new \RuntimeException('tee start fehlgeschlagen');
|
|
fwrite($pipes[0], $content);
|
|
fclose($pipes[0]);
|
|
stream_get_contents($pipes[1]);
|
|
stream_get_contents($pipes[2]);
|
|
if (proc_close($proc) !== 0) throw new \RuntimeException("tee failed: {$target}");
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.ui.security.tls-ciphers-form', ['presets' => array_keys(self::PRESETS)]);
|
|
}
|
|
}
|