diff --git a/app/Livewire/Ui/Security/Fail2banBanlist.php b/app/Livewire/Ui/Security/Fail2banBanlist.php index 13918ea..e4ae82c 100644 --- a/app/Livewire/Ui/Security/Fail2banBanlist.php +++ b/app/Livewire/Ui/Security/Fail2banBanlist.php @@ -4,7 +4,6 @@ namespace App\Livewire\Ui\Security; use Livewire\Attributes\On; use Livewire\Component; -use SQLite3; class Fail2banBanlist extends Component { @@ -135,23 +134,10 @@ class Fail2banBanlist extends Component /** Gibt permanent-Flag und verbleibende Zeit für (jail, ip) zurück. */ private function getBanInfo(string $jail, string $ip): array { - $db = $this->getDbFile(); $fallbackPermanent = ($jail === 'mailwolt-blacklist'); - if ($db === '') { - return ['permanent' => $fallbackPermanent, 'remaining' => '']; - } - - $q = sprintf( - "WITH last AS (SELECT MAX(timeofban) AS t FROM bans WHERE jail='%s' AND ip='%s') ". - "SELECT timeofban, bantime FROM bans, last ". - "WHERE jail='%s' AND ip='%s' AND timeofban=last.t LIMIT 1;", - SQLite3::escapeString($jail), SQLite3::escapeString($ip), - SQLite3::escapeString($jail), SQLite3::escapeString($ip) - ); - - $cmd = sprintf('sudo -n /usr/bin/sqlite3 -readonly %s %s 2>&1', - escapeshellarg($db), escapeshellarg($q)); + $cmd = sprintf('sudo -n /usr/local/sbin/clubird-f2b-baninfo %s %s 2>&1', + escapeshellarg($jail), escapeshellarg($ip)); $out = trim((string)@shell_exec($cmd)); if ($out !== '') { @@ -221,13 +207,4 @@ class Fail2banBanlist extends Component return (string) @shell_exec('sudo -n /usr/bin/fail2ban-client '.$args.' 2>&1'); } - /** Pfad zur Fail2Ban-SQLite-DB holen */ - private function getDbFile(): string - { - $out = $this->f2b('get dbfile'); - $lines = array_values(array_filter(array_map('trim', preg_split('/\r?\n/', $out)))); - $path = end($lines) ?: ''; - $path = preg_replace('/^`?-?\s*/', '', $path); - return $path ?: '/var/lib/fail2ban/fail2ban.sqlite3'; - } } diff --git a/scripts/clubird-f2b-baninfo b/scripts/clubird-f2b-baninfo new file mode 100644 index 0000000..d1ce625 --- /dev/null +++ b/scripts/clubird-f2b-baninfo @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +"""Read ban timing from fail2ban SQLite — called via sudo by www-data.""" +import sqlite3, sys, os + +if len(sys.argv) < 3: + sys.exit(1) + +jail = sys.argv[1] +ip = sys.argv[2] +db = '/var/lib/fail2ban/fail2ban.sqlite3' + +if not os.path.isfile(db): + sys.exit(1) + +try: + conn = sqlite3.connect(f'file:{db}?mode=ro', uri=True) + cur = conn.execute( + 'SELECT timeofban, bantime FROM bans WHERE jail=? AND ip=? ORDER BY timeofban DESC LIMIT 1', + (jail, ip) + ) + row = cur.fetchone() + conn.close() + if row: + print(f'{row[0]}|{row[1]}') +except Exception: + sys.exit(1) diff --git a/scripts/update.sh b/scripts/update.sh index e9c451c..865ca66 100644 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -286,21 +286,21 @@ UNIT # Fail2Ban: Jails + Sudoers sicherstellen (idempotent) local F2B_SUDO="/etc/sudoers.d/clubird-fail2ban" local F2B_JAIL="/etc/fail2ban/jail.d/clubird-jails.local" + _sbin_install "${APP_DIR}/scripts/clubird-f2b-baninfo" /usr/local/sbin/clubird-f2b-baninfo if command -v fail2ban-client >/dev/null 2>&1; then - # Sudoers für www-data + # Sudoers idempotent befüllen (fehlende Einträge werden nachgetragen) if [[ ! -f "$F2B_SUDO" ]]; then - cat > "$F2B_SUDO" << 'SUDOEOF' -www-data ALL=(root) NOPASSWD: /usr/bin/tee /etc/fail2ban/jail.d/00-defaults.local -www-data ALL=(root) NOPASSWD: /usr/bin/tee /etc/fail2ban/jail.d/whitelist.local -www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client reload -www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status -www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status * -www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client set * banip * -www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client set * unbanip * -SUDOEOF - chmod 440 "$F2B_SUDO" - echo "[✓] Fail2Ban sudoers installiert." + touch "$F2B_SUDO"; chmod 440 "$F2B_SUDO" fi + _sudoers_add "$F2B_SUDO" 'www-data ALL=(root) NOPASSWD: /usr/bin/tee /etc/fail2ban/jail.d/00-defaults.local' + _sudoers_add "$F2B_SUDO" 'www-data ALL=(root) NOPASSWD: /usr/bin/tee /etc/fail2ban/jail.d/whitelist.local' + _sudoers_add "$F2B_SUDO" 'www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client reload' + _sudoers_add "$F2B_SUDO" 'www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status' + _sudoers_add "$F2B_SUDO" 'www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status *' + _sudoers_add "$F2B_SUDO" 'www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client set * banip *' + _sudoers_add "$F2B_SUDO" 'www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client set * unbanip *' + _sudoers_add "$F2B_SUDO" 'www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client get *' + _sudoers_add "$F2B_SUDO" 'www-data ALL=(root) NOPASSWD: /usr/local/sbin/clubird-f2b-baninfo *' # Jail-Konfiguration (dovecot, postfix, nginx) if [[ ! -f "$F2B_JAIL" ]]; then cat > "$F2B_JAIL" << 'JAILEOF'