diff --git a/data/web/debug.php b/data/web/debug.php index 68b1a5a05..d99213cca 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 611043086..89ccd0374 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 7fac01e46..efde5ec53 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 9573baf15..07bf27b6d 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 dfd88d862..b294931cd 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 26ccbb25e..c9db5eb6a 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 1ff443b70..ba031d8e9 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 dc773150c..4191446ad 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';