From 5b5976ba230b6c31ed8811062446deb660d99eef Mon Sep 17 00:00:00 2001 From: andryyy Date: Sat, 15 Dec 2018 21:24:39 +0100 Subject: [PATCH] [Web] Show ratelimited messages, allow to delete Redis hash to reset status of a bucket --- data/web/debug.php | 19 ++++++++ data/web/inc/functions.inc.php | 36 ++++++++++----- data/web/inc/functions.ratelimit.inc.php | 39 ++++++++++++++++ data/web/js/admin.js | 2 +- data/web/js/debug.js | 58 ++++++++++++++++++++++++ data/web/json_api.php | 14 ++++++ data/web/lang/lang.de.php | 8 ++++ data/web/lang/lang.en.php | 8 ++++ 8 files changed, 172 insertions(+), 12 deletions(-) diff --git a/data/web/debug.php b/data/web/debug.php index 68b1a5a0..d99213cc 100644 --- a/data/web/debug.php +++ b/data/web/debug.php @@ -22,6 +22,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
  • Watchdog
  • ACME
  • API
  • +
  • Ratelimits
  • Rspamd
  • @@ -272,6 +273,24 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; +
    +
    +
    Ratelimits +
    + + + +
    +
    +
    +

    +
    +
    +
    +
    +
    +
    + diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 61104308..89ccd037 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -1263,7 +1263,7 @@ function get_u2f_registrations($username) { $sel->execute(array($username)); return $sel->fetchAll(PDO::FETCH_OBJ); } -function get_logs($container, $lines = false) { +function get_logs($application, $lines = false) { if ($lines === false) { $lines = $GLOBALS['LOG_LINES'] - 1; } @@ -1283,7 +1283,7 @@ function get_logs($container, $lines = false) { return false; } // SQL - if ($container == "mailcow-ui") { + if ($application == "mailcow-ui") { if (isset($from) && isset($to)) { $stmt = $pdo->prepare("SELECT * FROM `logs` ORDER BY `id` DESC LIMIT :from, :to"); $stmt->execute(array( @@ -1304,7 +1304,7 @@ function get_logs($container, $lines = false) { } } // Redis - if ($container == "dovecot-mailcow") { + if ($application == "dovecot-mailcow") { if (isset($from) && isset($to)) { $data = $redis->lRange('DOVECOT_MAILLOG', $from - 1, $to - 1); } @@ -1318,7 +1318,7 @@ function get_logs($container, $lines = false) { return $data_array; } } - if ($container == "postfix-mailcow") { + if ($application == "postfix-mailcow") { if (isset($from) && isset($to)) { $data = $redis->lRange('POSTFIX_MAILLOG', $from - 1, $to - 1); } @@ -1332,7 +1332,7 @@ function get_logs($container, $lines = false) { return $data_array; } } - if ($container == "sogo-mailcow") { + if ($application == "sogo-mailcow") { if (isset($from) && isset($to)) { $data = $redis->lRange('SOGO_LOG', $from - 1, $to - 1); } @@ -1346,7 +1346,7 @@ function get_logs($container, $lines = false) { return $data_array; } } - if ($container == "watchdog-mailcow") { + if ($application == "watchdog-mailcow") { if (isset($from) && isset($to)) { $data = $redis->lRange('WATCHDOG_LOG', $from - 1, $to - 1); } @@ -1360,7 +1360,7 @@ function get_logs($container, $lines = false) { return $data_array; } } - if ($container == "acme-mailcow") { + if ($application == "acme-mailcow") { if (isset($from) && isset($to)) { $data = $redis->lRange('ACME_LOG', $from - 1, $to - 1); } @@ -1374,7 +1374,21 @@ function get_logs($container, $lines = false) { return $data_array; } } - if ($container == "api-mailcow") { + if ($application == "ratelimited") { + if (isset($from) && isset($to)) { + $data = $redis->lRange('RL_LOG', $from - 1, $to - 1); + } + else { + $data = $redis->lRange('RL_LOG', 0, $lines); + } + if ($data) { + foreach ($data as $json_line) { + $data_array[] = json_decode($json_line, true); + } + return $data_array; + } + } + if ($application == "api-mailcow") { if (isset($from) && isset($to)) { $data = $redis->lRange('API_LOG', $from - 1, $to - 1); } @@ -1388,7 +1402,7 @@ function get_logs($container, $lines = false) { return $data_array; } } - if ($container == "netfilter-mailcow") { + if ($application == "netfilter-mailcow") { if (isset($from) && isset($to)) { $data = $redis->lRange('NETFILTER_LOG', $from - 1, $to - 1); } @@ -1402,7 +1416,7 @@ function get_logs($container, $lines = false) { return $data_array; } } - if ($container == "autodiscover-mailcow") { + if ($application == "autodiscover-mailcow") { if (isset($from) && isset($to)) { $data = $redis->lRange('AUTODISCOVER_LOG', $from - 1, $to - 1); } @@ -1416,7 +1430,7 @@ function get_logs($container, $lines = false) { return $data_array; } } - if ($container == "rspamd-history") { + if ($application == "rspamd-history") { $curl = curl_init(); curl_setopt($curl, CURLOPT_UNIX_SOCKET_PATH, '/var/lib/rspamd/rspamd.sock'); if (!is_numeric($lines)) { diff --git a/data/web/inc/functions.ratelimit.inc.php b/data/web/inc/functions.ratelimit.inc.php index 7fac01e4..efde5ec5 100644 --- a/data/web/inc/functions.ratelimit.inc.php +++ b/data/web/inc/functions.ratelimit.inc.php @@ -192,5 +192,44 @@ function ratelimit($_action, $_scope, $_data = null) { break; } break; + case 'delete': + $data['hash'] = $_data; + if ($_SESSION['mailcow_cc_role'] != 'admin' || !preg_match('/^RL[0-9A-Za-z=]+$/i', trim($data['hash']))) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), + 'msg' => 'access_denied' + ); + return false; + } + try { + if ($redis->exists($data['hash'])) { + $redis->delete($data['hash']); + $_SESSION['return'][] = array( + 'type' => 'success', + 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log), + 'msg' => 'hash_deleted' + ); + return true; + } + else { + $_SESSION['return'][] = array( + 'type' => 'warning', + 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log), + 'msg' => 'hash_not_found' + ); + return false; + } + } + catch (RedisException $e) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log), + 'msg' => array('redis_error', $e) + ); + return false; + } + return false; + break; } } \ No newline at end of file diff --git a/data/web/js/admin.js b/data/web/js/admin.js index 9573baf1..07bf27b6 100644 --- a/data/web/js/admin.js +++ b/data/web/js/admin.js @@ -8,7 +8,7 @@ jQuery(function($){ $("#rspamd_preset_1").on('click', function(e) { e.preventDefault(); $("form[data-id=rsetting]").find("#adminRspamdSettingsDesc").val(lang.rsettings_preset_1); - $("form[data-id=rsetting]").find("#adminRspamdSettingsContent").val('priority = 10;\nauthenticated = yes;\napply "default" {\n symbols_enabled = ["DKIM_SIGNED", "RATELIMIT_UPDATE", "RATELIMIT_CHECK", "DYN_RL_CHECK", "HISTORY_SAVE", "MILTER_HEADERS", "ARC_SIGNED"];\n}'); + $("form[data-id=rsetting]").find("#adminRspamdSettingsContent").val('priority = 10;\nauthenticated = yes;\napply "default" {\n symbols_enabled = ["DKIM_SIGNED", "RATELIMITED", "RATELIMIT_UPDATE", "RATELIMIT_CHECK", "DYN_RL_CHECK", "HISTORY_SAVE", "MILTER_HEADERS", "ARC_SIGNED"];\n}'); }); $("#rspamd_preset_2").on('click', function(e) { e.preventDefault(); diff --git a/data/web/js/debug.js b/data/web/js/debug.js index dfd88d86..b294931c 100644 --- a/data/web/js/debug.js +++ b/data/web/js/debug.js @@ -8,6 +8,8 @@ jQuery(function($){ var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="}; function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})} function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e'; } }); + } else if (table == 'rllog') { + $.each(data, function (i, item) { + if (item.user == null) { + item.user = "none"; + } + if (item.rl_hash == null) { + item.rl_hash = "err"; + } + item.indicator = '  '; + if (item.rl_hash != 'err') { + item.action = ' ' + lang.reset_limit + ''; + } + }); } return data }; @@ -575,6 +632,7 @@ jQuery(function($){ draw_watchdog_logs(); draw_acme_logs(); draw_api_logs(); + draw_rl_logs(); draw_ui_logs(); draw_netfilter_logs(); draw_rspamd_history(); diff --git a/data/web/json_api.php b/data/web/json_api.php index 26ccbb25..c9db5eb6 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -387,6 +387,17 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u } echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; break; + case "ratelimited": + // 0 is first record, so empty is fine + if (isset($extra)) { + $extra = preg_replace('/[^\d\-]/i', '', $extra); + $logs = get_logs('ratelimited', $extra); + } + else { + $logs = get_logs('ratelimited'); + } + echo (isset($logs) && !empty($logs)) ? json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '{}'; + break; case "netfilter": // 0 is first record, so empty is fine if (isset($extra)) { @@ -1043,6 +1054,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u case "admin": process_delete_return(admin('delete', array('username' => $items))); break; + case "rlhash": + echo ratelimit('delete', null, implode($items)); + break; } break; case "edit": diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index 1ff443b7..ba031d8e 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -556,10 +556,18 @@ $lang['admin']['no_record'] = 'Kein Eintrag'; $lang['admin']['filter_table'] = 'Tabelle Filtern'; $lang['admin']['empty'] = 'Keine Einträge vorhanden'; $lang['admin']['time'] = 'Zeit'; +$lang['admin']['last_applied'] = 'Zuletzt angewendet'; +$lang['admin']['reset_limit'] = 'Hash entfernen'; +$lang['admin']['hash_remove_info'] = 'Das Entfernen eines Ratelimit Hashes - sofern noch existent - bewirkt den Reset gezählter Nachrichten dieses Elements.
    + Jeder Hash wird durch eine eindeutige Farbe gekennzeichnet.'; +$lang['warning']['hash_not_found'] = 'Hash nicht gefunden'; +$lang['success']['hash_deleted'] = 'Hash wurde gelöscht'; +$lang['admin']['authed_user'] = 'Auth. Benutzer'; $lang['admin']['priority'] = 'Gewichtung'; $lang['admin']['refresh'] = 'Neu laden'; $lang['admin']['to_top'] = 'Nach oben'; $lang['admin']['in_use_by'] = 'Verwendet von'; +$lang['admin']['rate_name'] = 'Rate name'; $lang['admin']['message'] = 'Nachricht'; $lang['admin']['forwarding_hosts'] = 'Weiterleitungs-Hosts'; $lang['admin']['forwarding_hosts_hint'] = 'Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt, optional kann er aber in den Spam-Ordner einsortiert werden. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem mailcow-Server eingerichtet wurde.'; diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index dc773150..4191446a 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -580,8 +580,16 @@ $lang['admin']['no_record'] = 'No record'; $lang['admin']['filter_table'] = 'Filter table'; $lang['admin']['empty'] = 'No results'; $lang['admin']['time'] = 'Time'; +$lang['admin']['last_applied'] = 'Last applied'; +$lang['admin']['reset_limit'] = 'Remove hash'; +$lang['admin']['hash_remove_info'] = 'Removing a ratelimit hash (if still existing) will reset its counter completely.
    + Each hash is indicated by an individual color.'; +$lang['warning']['hash_not_found'] = 'Hash not found'; +$lang['success']['hash_deleted'] = 'Hash deleted'; +$lang['admin']['authed_user'] = 'Auth. user'; $lang['admin']['priority'] = 'Priority'; $lang['admin']['message'] = 'Message'; +$lang['admin']['rate_name'] = 'Rate name'; $lang['admin']['refresh'] = 'Refresh'; $lang['admin']['to_top'] = 'Back to top'; $lang['admin']['in_use_by'] = 'In use by';