From 89398c47263d3e1682ef30a4f38b87401a7fe7dd Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Tue, 20 Aug 2024 14:22:55 +0200 Subject: [PATCH 01/23] Before update on 2024-08-20_14_22_10 --- docker-compose.yml | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index cf0a028ff..16789083a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -613,36 +613,6 @@ services: aliases: - ofelia - ipv6nat-mailcow: - depends_on: - - unbound-mailcow - - mysql-mailcow - - redis-mailcow - - clamd-mailcow - - rspamd-mailcow - - php-fpm-mailcow - - sogo-mailcow - - dovecot-mailcow - - postfix-mailcow - - memcached-mailcow - - nginx-mailcow - - acme-mailcow - - netfilter-mailcow - - watchdog-mailcow - - dockerapi-mailcow - - solr-mailcow - environment: - - TZ=${TZ} - image: robbertkl/ipv6nat - security_opt: - - label=disable - restart: always - privileged: true - network_mode: "host" - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - /lib/modules:/lib/modules:ro - networks: mailcow-network: driver: bridge From 75f18df1435b72cb827af1f114f58de92c498f5e Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Fri, 23 Aug 2024 09:54:10 +0200 Subject: [PATCH 02/23] Revert "Before update on 2024-08-20_14_22_10" This reverts commit 89398c47263d3e1682ef30a4f38b87401a7fe7dd. --- docker-compose.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 16789083a..cf0a028ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -613,6 +613,36 @@ services: aliases: - ofelia + ipv6nat-mailcow: + depends_on: + - unbound-mailcow + - mysql-mailcow + - redis-mailcow + - clamd-mailcow + - rspamd-mailcow + - php-fpm-mailcow + - sogo-mailcow + - dovecot-mailcow + - postfix-mailcow + - memcached-mailcow + - nginx-mailcow + - acme-mailcow + - netfilter-mailcow + - watchdog-mailcow + - dockerapi-mailcow + - solr-mailcow + environment: + - TZ=${TZ} + image: robbertkl/ipv6nat + security_opt: + - label=disable + restart: always + privileged: true + network_mode: "host" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /lib/modules:/lib/modules:ro + networks: mailcow-network: driver: bridge From 37beed6ad93f259b97cad41877982bce95295629 Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Mon, 26 Aug 2024 09:56:49 +0200 Subject: [PATCH 03/23] update FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 11402129a..71cd7eda9 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ +github: mailcow custom: ["https://www.servercow.de/mailcow?lang=en#sal"] From 6550f0a3e889f5ee417a02e690cb577e1f8dc1d8 Mon Sep 17 00:00:00 2001 From: Habetdin <15926758+Habetdin@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:21:09 +0300 Subject: [PATCH 04/23] Only show active protocols on "last login" in mailbox overview --- data/web/js/site/mailbox.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/data/web/js/site/mailbox.js b/data/web/js/site/mailbox.js index af2862a37..5342eac39 100644 --- a/data/web/js/site/mailbox.js +++ b/data/web/js/site/mailbox.js @@ -894,7 +894,10 @@ jQuery(function($){ item.quota.value = humanFileSize(item.quota_used) + "/" + item.quota.value; item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox); - item.last_mail_login = item.last_imap_login + '/' + item.last_pop3_login + '/' + item.last_smtp_login + '/' + item.last_sso_login; + item.last_mail_login = (item.attributes.imap_access == 1 ? '
IMAP @ ' + unix_time_format(Number(item.last_imap_login)) + '

' : '') + + (item.attributes.pop3_access == 1 ? '
POP3 @ ' + unix_time_format(Number(item.last_pop3_login)) + '

' : '') + + (item.attributes.smtp_access == 1 ? '
SMTP @ ' + unix_time_format(Number(item.last_smtp_login)) + '

' : '') + + '
SSO @ ' + unix_time_format(Number(item.last_sso_login)) + '
'; /* if (!item.rl) { item.rl = '∞'; @@ -1010,14 +1013,7 @@ jQuery(function($){ data: 'last_mail_login', searchable: false, defaultContent: '', - responsivePriority: 7, - render: function (data, type) { - res = data.split("/"); - return '
IMAP @ ' + unix_time_format(Number(res[0])) + '

' + - '
POP3 @ ' + unix_time_format(Number(res[1])) + '

' + - '
SMTP @ ' + unix_time_format(Number(res[2])) + '

' + - '
SSO @ ' + unix_time_format(Number(res[3])) + '
'; - } + responsivePriority: 7 }, { title: lang.last_pw_change, From 4d688c55007216e50434060398ee91b2755e25d6 Mon Sep 17 00:00:00 2001 From: Niklas Meyer Date: Tue, 12 Nov 2024 15:57:17 +0100 Subject: [PATCH 05/23] 2024-11a (#6160) * update.sh: precaution ask for deletion of dns_blocklists.cf if old format (#6154) * [Web] Updated lang.zh-cn.json (#6151) [Web] Updated lang.zh-cn.json Co-authored-by: Easton Man * compose: bump sogo version to include 5.11.2 (#6156) * php: use correct php image + workaround of #6149 (#6159) * compose: bump php-fpm container to correctly use patched c-ares * [Web] check $containers_info contains required fields --------- Co-authored-by: FreddleSpl0it --------- Co-authored-by: milkmaker Co-authored-by: Easton Man Co-authored-by: FreddleSpl0it --- data/web/debug.php | 22 +++--- data/web/lang/lang.zh-cn.json | 145 ++++++++++++++++++++++++++++------ docker-compose.yml | 4 +- update.sh | 30 +++++++ 4 files changed, 168 insertions(+), 33 deletions(-) diff --git a/data/web/debug.php b/data/web/debug.php index 9c338009c..4a099cb6e 100644 --- a/data/web/debug.php +++ b/data/web/debug.php @@ -23,11 +23,15 @@ $exec_fields = array('cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail'); $vmail_df = explode(',', (string)json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true)); // containers -$containers = (array) docker('info'); -if ($clamd_status === false) unset($containers['clamd-mailcow']); -if ($solr_status === false) unset($containers['solr-mailcow']); -ksort($containers); -foreach ($containers as $container => $container_info) { +$containers_info = (array) docker('info'); +if ($clamd_status === false) unset($containers_info['clamd-mailcow']); +if ($solr_status === false) unset($containers_info['solr-mailcow']); +ksort($containers_info); +$containers = array(); +foreach ($containers_info as $container => $container_info) { + if (!isset($container_info['State']) || !is_array($container_info['State']) || !isset($container_info['State']['StartedAt'])){ + continue; + } date_default_timezone_set('UTC'); $StartedAt = date_parse($container_info['State']['StartedAt']); if ($StartedAt['hour'] !== false) { @@ -42,15 +46,15 @@ foreach ($containers as $container => $container_info) { try { $user_tz = new DateTimeZone(getenv('TZ')); $date->setTimezone($user_tz); - $started = $date->format('r'); + $container_info['State']['StartedAtHR'] = $date->format('r'); } catch(Exception $e) { - $started = '?'; + $container_info['State']['StartedAtHR'] = '?'; } } else { - $started = '?'; + $container_info['State']['StartedAtHR'] = '?'; } - $containers[$container]['State']['StartedAtHR'] = $started; + $containers[$container] = $container_info; } // get mailcow data diff --git a/data/web/lang/lang.zh-cn.json b/data/web/lang/lang.zh-cn.json index bac42e5d7..ca6c9aaf2 100644 --- a/data/web/lang/lang.zh-cn.json +++ b/data/web/lang/lang.zh-cn.json @@ -63,7 +63,7 @@ "exclude": "拒绝对象 (Regex)", "full_name": "全称", "gal": "全球地址簿", - "gal_info": "全球地址簿包含了域名下的所有对象,并且此行为不能被用户更改。如果关闭,用户的 \"空闲/繁忙\" 的状态将无法在 SOGo 中显示。 重启 SOGo 服务以应用更改。", + "gal_info": "全球地址簿包含了域名下的所有对象,并且此行为不能被用户更改。如果关闭,用户的 \"空闲/繁忙\" 的状态将无法在 SOGo 中显示。 重启 SOGo 服务以应用更改。", "generate": "生成", "goto_ham": "学习为非垃圾邮件", "goto_null": "静默丢弃邮件", @@ -146,7 +146,7 @@ "arrival_time": "到达时间 (服务器时间)", "authed_user": "已认证用户", "ays": "确定继续操作?", - "ban_list_info": "以下为被封禁的 IP 列表: 网络 (剩余封禁时间) - [操作]
被取消封禁的 IP 将会在几秒之内从封禁列表中移除
红色标签表示因黑名单而导致的永久封禁", + "ban_list_info": "以下为被封禁的 IP 列表: 网络 (剩余封禁时间) - [操作]
被取消封禁的 IP 将会在几秒之内从封禁列表中移除
红色标签表示因黑名单而导致的永久封禁。", "change_logo": "更改 Logo", "configuration": "配置", "convert_html_to_text": "将 HTML 转换为纯文本内容", @@ -292,7 +292,7 @@ "rsettings_preset_2": "允许管理员接收垃圾邮件", "rsettings_preset_3": "只允许指定的发件人发信 (例如只允许内部邮箱发送)", "rsettings_preset_4": "禁用域名的 Rspamd 服务", - "rspamd_com_settings": "设置名称将会自动生成,请看参考下方的示例预设。查看Rspamd 文档以了解更多的细节。", + "rspamd_com_settings": "设置名称将会自动生成,请看参考下方的示例预设。查看Rspamd 文档以了解更多的细节", "rspamd_global_filters": "全局过滤规则", "rspamd_global_filters_agree": "我会小心谨慎的!", "rspamd_global_filters_info": "全局过滤规则包含了不同类型的全局黑名单和白名单。", @@ -353,7 +353,7 @@ "password_reset_tmpl_html": "HTML 模版", "password_reset_tmpl_text": "文字模版", "password_settings": "密码设定", - "restore_template": "留空以恢复预设模版", + "restore_template": "留空以恢复预设模版。", "ip_check": "IP 检查", "ip_check_disabled": "IP 检查已禁用。你可透过以下路径启用
系统 > 配置 > 选项 > 页面自定义", "queue_unban": "解除封禁", @@ -481,7 +481,21 @@ "template_exists": "模板 %s 已存在", "template_name_invalid": "模板名称无效", "cors_invalid_method": "制定的允许模式无效", - "cors_invalid_origin": "指定的允许原无效" + "cors_invalid_origin": "指定的允许原无效", + "reset_token_limit_exceeded": "Reset token 数量已达上限。请稍后再尝试。", + "extended_sender_acl_denied": "缺少设置外部发送者地址的 ACL", + "img_dimensions_exceeded": "图片超出最大图片大小", + "img_size_exceeded": "图片超过最大文件大小", + "invalid_reset_token": "不合法的 reset token", + "password_reset_invalid_user": "未找到此信箱或未设置恢复邮箱", + "password_reset_na": "密码恢复目前无法使用。请联系您的管理员。", + "recovery_email_failed": "无法发送恢复邮件。请联系您的管理员。", + "template_id_invalid": "Template ID %s 无效", + "to_invalid": "收件人不能为空", + "webauthn_authenticator_failed": "找不到所选的 authenticator", + "webauthn_publickey_failed": "没有为选定的身份验证器保存公钥", + "webauthn_username_failed": "所选的 authenticator 属于另一个账户", + "demo_mode_enabled": "演示模式已开启" }, "debug": { "chart_this_server": "图表 (此服务器)", @@ -516,7 +530,13 @@ "error_show_ip": "无法解析公网IP地址", "show_ip": "显示公网IP", "update_available": "有可用更新", - "update_failed": "无法检查更新" + "update_failed": "无法检查更新", + "architecture": "结构", + "container_stopped": "已停止", + "current_time": "系统时间", + "timezone": "时区", + "no_update_available": "系统已经是最新版本", + "wip": "正在施工中" }, "diagnostics": { "cname_from_a": "来自 A/AAAA 记录的值。但只要记录指向正确的资源即可。", @@ -640,7 +660,31 @@ "title": "编辑对象", "unchanged_if_empty": "如果不更改则留空", "username": "用户名", - "validate_save": "验证并保存" + "validate_save": "验证并保存", + "domain_footer_info_vars": { + "from_name": "{= from_name =} - 信件中的 From name 字段,例如对于 \"Mailcow <moo@mailcow.tld>\" 来说它是 \"Mailcow\"", + "auth_user": "{= auth_user =} - MTA 指定的经过认证的用户名", + "from_user": "{= from_user =} - 信件中的 From user 字段,例如对于 \"moo@mailcow.tld\" 来说它是 \"moo\"", + "from_addr": "{= from_addr =} - 信件中的 From address 字段", + "from_domain": "{= from_domain =} - 信件中的 From domain 字段", + "custom": "{= foo =} - 如果信箱有一个自定义属性 \"foo\" 的值为 \"bar\", 它将返回 \"bar\"" + }, + "created_on": "创建于", + "custom_attributes": "自定义属性", + "mailbox_rename": "重命名信箱", + "mailbox_rename_agree": "我已经创建了一个备份。", + "mailbox_rename_warning": "重要! 重命名信箱之前请先创建备份。", + "mailbox_rename_alias": "自动创建别名", + "mailbox_rename_title": "新本地信箱的名字", + "password_recovery_email": "密码重置邮箱", + "domain_footer": "域页脚", + "domain_footer_html": "HTML footer", + "domain_footer_info": "Domain-wide footers 会被加入到该 domain 下的地址发出的所有邮件中。
下列变量可以在 footer 中使用:", + "domain_footer_plain": "纯文字 footer", + "domain_footer_skip_replies": "在回信中忽略 footer", + "footer_exclude": "从 footer 中排除", + "last_modified": "上次修改时间", + "pushover_sound": "声音" }, "fido2": { "confirm": "确认", @@ -675,13 +719,14 @@ "header": { "administration": "配置和管理", "apps": "应用", - "debug": "系统信息", + "debug": "信息", "email": "E-Mail", "mailcow_config": "配置", "quarantine": "隔离", "restart_netfilter": "重启 netfilter", "restart_sogo": "重启 SOGo", - "user_settings": "用户设置" + "user_settings": "用户设置", + "mailcow_system": "系统" }, "info": { "awaiting_tfa_confirmation": "等待 TFA 确认", @@ -695,7 +740,14 @@ "mobileconfig_info": "请使用邮箱用户登录以下载 Apple 连接描述文件。", "other_logins": "Key 登录", "password": "密码", - "username": "用户名" + "username": "用户名", + "forgot_password": "> 忘记密码?", + "back_to_mailcow": "返回到 mailcow", + "new_password": "新密码", + "new_password_confirm": "确认新密码", + "reset_password": "重置密码", + "request_reset_password": "请求重置密码", + "invalid_pass_reset_token": "密码重置 token 无效或已过期。
请重新获取新的密码重置链接。" }, "mailbox": { "action": "操作", @@ -799,9 +851,9 @@ "recipient_map": "收件人映射", "recipient_map_info": "收件人映射用于在邮件被发送前替换收件人的地址。", "recipient_map_new": "新收件人", - "recipient_map_new_info": "新收件人必须为合法的邮箱地址", + "recipient_map_new_info": "新收件人必须为合法的邮箱地址。", "recipient_map_old": "原收件人", - "recipient_map_old_info": "原收件人必须为合法的邮箱地址", + "recipient_map_old_info": "原收件人必须为合法的邮箱地址。", "recipient_maps": "收件人映射", "relay_all": "中继所有收件人", "remove": "删除", @@ -818,7 +870,7 @@ "sieve_preset_5": "自动回复 (休假)", "sieve_preset_6": "拒绝接收邮件并通知", "sieve_preset_7": "重定向邮件并保留或删除", - "sieve_preset_8": "删除发件人发送给自己别名地址的邮件", + "sieve_preset_8": "重定向来自特定发件人的邮件,标记为已读并放入子文件夹中", "sieve_preset_header": "请看下方的示例预设。 查看 Sieve Wikipedia 页面 (英文)以了解更多细节。", "sogo_visible": "SOGo 别名显示", "sogo_visible_n": "在 SOGo 中隐藏别名", @@ -860,10 +912,20 @@ "mailbox_templates": "邮箱模板", "gal": "全局地址列表", "max_aliases": "最大别名数", - "max_mailboxes": "最大可能的邮箱数" + "max_mailboxes": "最大可能的邮箱数", + "created_on": "建立于", + "force_pw_update": "强制在下一次登陆时更新密码", + "add_template": "新增模板", + "goto_ham": "学习为 非垃圾邮件 ", + "goto_spam": "学习为 垃圾邮件 ", + "last_modified": "上次修改时间", + "max_quota": "每个信箱的最大容量配额", + "relay_unknown": "转发未知信箱", + "templates": "模板", + "template": "模板" }, "oauth2": { - "access_denied": "请作为邮箱所有者登录以使用 OAuth2 授权", + "access_denied": "请作为邮箱所有者登录以使用 OAuth2 授权。", "authorize_app": "授权应用", "deny": "拒绝", "permit": "授权应用", @@ -926,7 +988,19 @@ }, "queue": { "queue_manager": "队列管理器", - "delete": "全部删除" + "delete": "全部删除", + "info": "邮件队列包含所有等待投递的邮件。如果邮件长时间停留在邮件队列中,系统会自动将其删除。
对应的邮件错误信息会提供有关邮件无法送达的原因。", + "flush": "刷新队列", + "legend": "邮件队列操作功能:", + "ays": "请确认您要删除当前队列中的所有项目。", + "deliver_mail": "投递", + "deliver_mail_legend": "尝试重新投递选中的邮件。", + "hold_mail": "保留", + "hold_mail_legend": "保持选中的邮件。(不继续投递)", + "show_message": "显示内容", + "unban": "队列解除限制", + "unhold_mail": "取消保持", + "unhold_mail_legend": "允许选中的邮件继续投递。(需要在保持状态)" }, "ratelimit": { "disabled": "禁用", @@ -994,7 +1068,7 @@ "nginx_reloaded": "Nginx 已重新启动", "object_modified": "已保存对象 %s 更改", "password_policy_saved": "已成功保存密码规则", - "pushover_settings_edited": "已成功设置 Pushover,请重新校验凭证", + "pushover_settings_edited": "Pushover 设置已保存,请重新校验凭证。", "qlearn_spam": "消息 ID %s 已被学习为垃圾邮件并被删除", "queue_command_success": "成功执行配额命令", "recipient_map_entry_deleted": "已删除接收人映射 ID %s", @@ -1018,7 +1092,17 @@ "verified_fido2_login": "FIDO2 登录验证成功", "verified_totp_login": "TOTP 登录验证成功", "verified_webauthn_login": "WebAuthn 登录验证成功", - "verified_yotp_login": "Yubico OTP 登录验证成功" + "verified_yotp_login": "Yubico OTP 登录验证成功", + "domain_footer_modified": "对域 footer %s 的修改已保存", + "cors_headers_edited": "CORS 设置已保存", + "ip_check_opt_in_modified": "IP 检查已保存", + "f2b_banlist_refreshed": "黑名单 ID 已成功刷新。", + "mailbox_renamed": "信箱 %s 已被重命名为 %s", + "password_changed_success": "密码重置成功", + "recovery_email_sent": "重置邮件已发送至 %s", + "template_added": "新增了模板 %s", + "template_modified": "模板 %s 的修改已保存", + "template_removed": "模板 ID %s 已删除" }, "tfa": { "api_register": "%s 使用了 Yubico Cloud API,请在此为你的密钥获取 API 密钥", @@ -1045,7 +1129,8 @@ "webauthn": "WebAuthn 认证", "waiting_usb_auth": "等待 USB 设备中...

现在请触碰你的 WebAuthn USB 设备上的按钮。", "waiting_usb_register": "等待 USB 设备中...

请在上方输入你的密码并请触碰你的 WebAuthn USB 设备上的按钮以确认注册该 WebAuthn 设备。", - "yubi_otp": "Yubico OTP 认证" + "yubi_otp": "Yubico OTP 认证", + "authenticators": "验证器(Authenticators)" }, "user": { "action": "操作", @@ -1062,7 +1147,7 @@ "alias_valid_until": "有效至", "aliases_also_send_as": "同时允许发送为", "aliases_send_as_all": "已关闭发件人可访性检查的域名和域名别名", - "app_hint": "应用密码是你登录 IMAP 和 SMTP 时的可选替代密码,用户名仍然保持不变。
应用密码不适用于 SOGo (包括 ActiveSync) ", + "app_hint": "应用密码是你登录 IMAP 和 SMTP 时的可选替代密码,用户名仍然保持不变。
应用密码不适用于 SOGo (包括 ActiveSync)。", "allowed_protocols": "允许使用的协议", "app_name": "应用名称", "app_passwds": "应用密码", @@ -1206,7 +1291,12 @@ "weeks": "周", "with_app_password": "包含应用密码", "year": "年", - "years": "年" + "years": "年", + "pw_recovery_email": "密码重置邮箱", + "password_reset_info": "如果不提供密码重置邮箱,此功能将无法使用。", + "pushover_sound": "声音", + "value": "值", + "attribute": "属性" }, "warning": { "cannot_delete_self": "不能删除已登录的用户", @@ -1233,6 +1323,17 @@ "last": "最后一页", "previous": "上一页", "next": "下一页" - } + }, + "aria": { + "sortDescending": ": 激活以降序对列进行排序", + "sortAscending": ": 激活以升序对列进行排序" + }, + "decimal": ".", + "emptyTable": "表中没有可用的数据", + "infoFiltered": "(从 _MAX_ 个总条目中过滤)", + "thousands": ",", + "lengthMenu": "显示 _MENU_ 条目", + "loadingRecords": "加载中...", + "zeroRecords": "未找到符合条件的记录" } } diff --git a/docker-compose.yml b/docker-compose.yml index c462ba88c..b0324521a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -112,7 +112,7 @@ services: - rspamd php-fpm-mailcow: - image: mailcow/phpfpm:1.91 + image: mailcow/phpfpm:1.91.1 command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" depends_on: - redis-mailcow @@ -177,7 +177,7 @@ services: - phpfpm sogo-mailcow: - image: mailcow/sogo:1.127 + image: mailcow/sogo:1.127.1 environment: - DBNAME=${DBNAME} - DBUSER=${DBUSER} diff --git a/update.sh b/update.sh index fe8aee72f..cf94eb71c 100755 --- a/update.sh +++ b/update.sh @@ -275,6 +275,34 @@ detect_bad_asn() { fi } +fix_broken_dnslist_conf() { + +# Fixing issue: #6143. To be removed in a later patch + + local file="${SCRIPT_DIR}/data/conf/postfix/dns_blocklists.cf" + # Check if the file exists + if [[ ! -f "$file" ]]; then + return 1 + fi + + # Check if the file contains the autogenerated comment + if grep -q "# Autogenerated by mailcow" "$file"; then + # Ask the user if custom changes were made + echo -e "\e[91mWARNING!!! \e[31mAn old version of dns_blocklists.cnf has been detected which may cause a broken postfix upon startup (see: https://github.com/mailcow/mailcow-dockerized/issues/6143)...\e[0m" + echo -e "\e[31mIf you have any custom settings in there you might copy it away and adapt the changes after the file is regenerated...\e[0m" + read -p "Do you want to delete the file now and let mailcow regenerate it properly? " response + if [[ "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then + rm "$file" + echo -e "\e[32mdns_blocklists.cf has been deleted and will be properly regenerated" + return 0 + else + echo -e "\e[35mOk, not deleting it! Please make sure you take a look at postfix upon start then..." + return 2 + fi + fi + +} + ############## End Function Section ############## # Check permissions @@ -437,6 +465,8 @@ source mailcow.conf detect_docker_compose_command +fix_broken_dnslist_conf + DOTS=${MAILCOW_HOSTNAME//[^.]}; if [ ${#DOTS} -lt 1 ]; then echo -e "\e[31mMAILCOW_HOSTNAME (${MAILCOW_HOSTNAME}) is not a FQDN!\e[0m" From b9f52df3f10e43c1fc9364ff0ef5a6008d959a40 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Wed, 13 Nov 2024 12:04:49 +0100 Subject: [PATCH 06/23] [Web] update _sogo_static_view on password reset --- data/web/inc/functions.inc.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index fd6c7fc2f..124683dbf 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -2515,6 +2515,8 @@ function reset_password($action, $data = null) { ':username' => $username )); + update_sogo_static_view($username); + $_SESSION['return'][] = array( 'type' => 'success', 'log' => array(__FUNCTION__, $action, $_data_log), From 6d1f7482edae6927fa1b2b540d5ef11fa8e2bcb2 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Wed, 13 Nov 2024 10:42:38 +0100 Subject: [PATCH 07/23] [Web] broadcast maildir move to dovecot containers on mailbox_rename --- data/web/inc/functions.mailbox.inc.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 9de665675..73c115411 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -3351,7 +3351,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'old_maildir' => $domain . '/' . $old_local_part, 'new_maildir' => $domain . '/' . $new_local_part ); - docker('post', 'dovecot-mailcow', 'exec', $exec_fields); + if (getenv("CLUSTERMODE") == "replication") { + // broadcast to each dovecot container + docker('broadcast', 'dovecot-mailcow', 'exec', $exec_fields); + } else { + docker('post', 'dovecot-mailcow', 'exec', $exec_fields); + } // rename username in sogo $exec_fields = array( From d10d64dd9216ca0d0f70280a3c7a4c12bbc463dc Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Fri, 15 Nov 2024 16:07:18 +0100 Subject: [PATCH 08/23] mysql: increased thread_stack to 192k since 10.5.27 --- data/conf/mysql/my.cnf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/conf/mysql/my.cnf b/data/conf/mysql/my.cnf index b4c348863..489b973c4 100644 --- a/data/conf/mysql/my.cnf +++ b/data/conf/mysql/my.cnf @@ -20,7 +20,7 @@ thread_cache_size = 8 query_cache_type = 0 query_cache_size = 0 max_heap_table_size = 48M -thread_stack = 128K +thread_stack = 192K skip-host-cache skip-name-resolve log-warnings = 0 From 5ad4ab5b60b5f4496d01b623dca44d862cfff868 Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Fri, 15 Nov 2024 16:39:06 +0100 Subject: [PATCH 09/23] update.sh: fixed typos --- update.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/update.sh b/update.sh index cf94eb71c..3afe623c2 100755 --- a/update.sh +++ b/update.sh @@ -288,9 +288,9 @@ fix_broken_dnslist_conf() { # Check if the file contains the autogenerated comment if grep -q "# Autogenerated by mailcow" "$file"; then # Ask the user if custom changes were made - echo -e "\e[91mWARNING!!! \e[31mAn old version of dns_blocklists.cnf has been detected which may cause a broken postfix upon startup (see: https://github.com/mailcow/mailcow-dockerized/issues/6143)...\e[0m" + echo -e "\e[91mWARNING!!! \e[31mAn old version of dns_blocklists.cf has been detected which may cause a broken postfix upon startup (see: https://github.com/mailcow/mailcow-dockerized/issues/6143)...\e[0m" echo -e "\e[31mIf you have any custom settings in there you might copy it away and adapt the changes after the file is regenerated...\e[0m" - read -p "Do you want to delete the file now and let mailcow regenerate it properly? " response + read -p "Do you want to delete the file now and let mailcow regenerate it properly? [y/n]" response if [[ "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then rm "$file" echo -e "\e[32mdns_blocklists.cf has been deleted and will be properly regenerated" From 70ca5fde9520f78414b66b2db789a0524cb4975f Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Tue, 19 Nov 2024 08:39:52 +0100 Subject: [PATCH 10/23] [Nginx] Use jinja2 for templating nginx configuration --- data/Dockerfiles/nginx/Dockerfile | 18 ++ data/Dockerfiles/nginx/bootstrap.py | 76 +++++ data/Dockerfiles/nginx/docker-entrypoint.sh | 26 ++ data/conf/nginx/000-map-size.conf | 3 - data/conf/nginx/dynmaps.conf | 19 -- data/conf/nginx/includes/site-defaults.conf | 242 --------------- data/conf/nginx/includes/sogo_proxy_auth.conf | 8 - data/conf/nginx/meta_exporter.conf | 19 -- data/conf/nginx/nginx.conf.j2 | 119 ++++++++ data/conf/nginx/site.conf | 10 - data/conf/nginx/sites-default.conf.j2 | 276 ++++++++++++++++++ .../nginx/templates/listen_plain.template | 2 - data/conf/nginx/templates/listen_ssl.template | 3 - .../nginx/templates/server_name.template.sh | 1 - data/conf/nginx/templates/sites.template.sh | 38 --- data/conf/nginx/templates/sogo.template | 1 - .../conf/nginx/templates/sogo_eas.template.sh | 5 - docker-compose.yml | 29 +- 18 files changed, 526 insertions(+), 369 deletions(-) create mode 100644 data/Dockerfiles/nginx/Dockerfile create mode 100644 data/Dockerfiles/nginx/bootstrap.py create mode 100755 data/Dockerfiles/nginx/docker-entrypoint.sh delete mode 100644 data/conf/nginx/000-map-size.conf delete mode 100644 data/conf/nginx/dynmaps.conf delete mode 100644 data/conf/nginx/includes/site-defaults.conf delete mode 100644 data/conf/nginx/includes/sogo_proxy_auth.conf delete mode 100644 data/conf/nginx/meta_exporter.conf create mode 100644 data/conf/nginx/nginx.conf.j2 delete mode 100644 data/conf/nginx/site.conf create mode 100644 data/conf/nginx/sites-default.conf.j2 delete mode 100644 data/conf/nginx/templates/listen_plain.template delete mode 100644 data/conf/nginx/templates/listen_ssl.template delete mode 100755 data/conf/nginx/templates/server_name.template.sh delete mode 100644 data/conf/nginx/templates/sites.template.sh delete mode 100644 data/conf/nginx/templates/sogo.template delete mode 100644 data/conf/nginx/templates/sogo_eas.template.sh diff --git a/data/Dockerfiles/nginx/Dockerfile b/data/Dockerfiles/nginx/Dockerfile new file mode 100644 index 000000000..7d2ce34f3 --- /dev/null +++ b/data/Dockerfiles/nginx/Dockerfile @@ -0,0 +1,18 @@ +FROM nginx:alpine +LABEL maintainer "The Infrastructure Company GmbH " + +ENV PIP_BREAK_SYSTEM_PACKAGES=1 + +RUN apk add --no-cache nginx \ + python3 \ + py3-pip && \ + pip install --upgrade pip && \ + pip install Jinja2 + +RUN mkdir -p /etc/nginx/includes + +COPY ./bootstrap.py / +COPY ./docker-entrypoint.sh / + +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["nginx", "-g", "daemon off;"] diff --git a/data/Dockerfiles/nginx/bootstrap.py b/data/Dockerfiles/nginx/bootstrap.py new file mode 100644 index 000000000..0d24ae9b2 --- /dev/null +++ b/data/Dockerfiles/nginx/bootstrap.py @@ -0,0 +1,76 @@ +import os +import subprocess +from jinja2 import Environment, FileSystemLoader + + +def sites_default_conf(env, template_vars): + config_name = "sites-default.conf" + template = env.get_template(f"{config_name}.j2") + config = template.render(template_vars) + + with open(f"/etc/nginx/includes/{config_name}", "w") as f: + f.write(config) + +def nginx_conf(env, template_vars): + config_name = "nginx.conf" + template = env.get_template(f"{config_name}.j2") + config = template.render(template_vars) + + with open(f"/etc/nginx/{config_name}", "w") as f: + f.write(config) + +def prepare_template_vars(): + template_vars = { + 'IPV4_NETWORK': os.getenv("IPV4_NETWORK", "172.22.1"), + 'TRUSTED_NETWORK': os.getenv("TRUSTED_NETWORK", False), + 'SKIP_RSPAMD': os.getenv("SKIP_RSPAMD", "n").lower() in ("y", "yes"), + 'SKIP_SOGO': os.getenv("SKIP_SOGO", "n").lower() in ("y", "yes"), + 'NGINX_USE_PROXY_PROTOCOL': os.getenv("NGINX_USE_PROXY_PROTOCOL", "n").lower() in ("y", "yes"), + 'MAILCOW_HOSTNAME': os.getenv("MAILCOW_HOSTNAME", ""), + 'ADDITIONAL_SERVER_NAMES': os.getenv("ADDITIONAL_SERVER_NAMES", "").replace(',', ' '), + 'HTTP_PORT': os.getenv("HTTP_PORT", "80"), + 'HTTPS_PORT': os.getenv("HTTPS_PORT", "443"), + 'SOGOHOST': os.getenv("SOGOHOST", "sogo-mailcow"), + 'RSPAMDHOST': os.getenv("RSPAMDHOST", "rspamd-mailcow"), + 'PHPFPMHOST': os.getenv("PHPFPMHOST", "php-fpm-mailcow"), + } + + ssl_dir = '/etc/ssl/mail/' + template_vars['valid_cert_dirs'] = [] + for d in os.listdir(ssl_dir): + full_path = os.path.join(ssl_dir, d) + if not os.path.isdir(full_path): + continue + + cert_path = os.path.join(full_path, 'cert.pem') + key_path = os.path.join(full_path, 'key.pem') + domains_path = os.path.join(full_path, 'domains') + + if os.path.isfile(cert_path) and os.path.isfile(key_path) and os.path.isfile(domains_path): + with open(domains_path, 'r') as file: + domains = file.read().strip() + domains_list = domains.split() + if domains_list and template_vars["MAILCOW_HOSTNAME"] not in domains_list: + template_vars['valid_cert_dirs'].append({ + 'cert_path': full_path + '/', + 'domains': domains + }) + + return template_vars + +def main(): + env = Environment(loader=FileSystemLoader('./etc/nginx/conf.d')) + + # Render config + print("Render config") + template_vars = prepare_template_vars() + sites_default_conf(env, template_vars) + nginx_conf(env, template_vars) + + # Validate config + print("Validate config") + subprocess.run(["nginx", "-qt"]) + + +if __name__ == "__main__": + main() diff --git a/data/Dockerfiles/nginx/docker-entrypoint.sh b/data/Dockerfiles/nginx/docker-entrypoint.sh new file mode 100755 index 000000000..beb0b4d9e --- /dev/null +++ b/data/Dockerfiles/nginx/docker-entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +until ping ${REDISHOST} -c1 > /dev/null; do + echo "Waiting for Redis..." + sleep 1 +done +until ping ${PHPFPMHOST} -c1 > /dev/null; do + echo "Waiting for PHP..." + sleep 1 +done +if printf "%s\n" "${SKIP_SOGO}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then + until ping ${SOGOHOST} -c1 > /dev/null; do + echo "Waiting for SOGo..." + sleep 1 + done +fi +if printf "%s\n" "${SKIP_RSPAMD}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then + until ping ${RSPAMDHOST} -c1 > /dev/null; do + echo "Waiting for Rspamd..." + sleep 1 + done +fi + +python3 /bootstrap.py + +exec "$@" diff --git a/data/conf/nginx/000-map-size.conf b/data/conf/nginx/000-map-size.conf deleted file mode 100644 index a834306ed..000000000 --- a/data/conf/nginx/000-map-size.conf +++ /dev/null @@ -1,3 +0,0 @@ -map_hash_max_size 256; -map_hash_bucket_size 256; - diff --git a/data/conf/nginx/dynmaps.conf b/data/conf/nginx/dynmaps.conf deleted file mode 100644 index 99c0c6aaa..000000000 --- a/data/conf/nginx/dynmaps.conf +++ /dev/null @@ -1,19 +0,0 @@ -server { - listen 8081; - listen [::]:8081; - index index.php index.html; - server_name _; - error_log /var/log/nginx/error.log; - access_log /var/log/nginx/access.log; - root /dynmaps; - - location ~ \.php$ { - try_files $uri =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass phpfpm:9001; - fastcgi_index index.php; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - } -} diff --git a/data/conf/nginx/includes/site-defaults.conf b/data/conf/nginx/includes/site-defaults.conf deleted file mode 100644 index 1d03e9398..000000000 --- a/data/conf/nginx/includes/site-defaults.conf +++ /dev/null @@ -1,242 +0,0 @@ - - include /etc/nginx/mime.types; - charset utf-8; - override_charset on; - - server_tokens off; - - ssl_protocols TLSv1.2 TLSv1.3; - ssl_prefer_server_ciphers on; - ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; - ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; - ssl_session_cache shared:SSL:50m; - ssl_session_timeout 1d; - ssl_session_tickets off; - - add_header Strict-Transport-Security "max-age=15768000;"; - add_header X-Content-Type-Options nosniff; - add_header X-XSS-Protection "1; mode=block"; - add_header X-Robots-Tag none; - add_header X-Download-Options noopen; - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Permitted-Cross-Domain-Policies none; - add_header Referrer-Policy strict-origin; - - index index.php index.html; - - client_max_body_size 0; - - gzip on; - gzip_disable "msie6"; - - gzip_vary on; - gzip_proxied off; - gzip_comp_level 6; - gzip_buffers 16 8k; - gzip_http_version 1.1; - gzip_min_length 256; - gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon; - - location ~ ^/(fonts|js|css|img)/ { - expires max; - add_header Cache-Control public; - } - - error_log /var/log/nginx/error.log; - access_log /var/log/nginx/access.log; - fastcgi_hide_header X-Powered-By; - absolute_redirect off; - root /web; - - location / { - try_files $uri $uri/ @strip-ext; - } - - location /qhandler { - rewrite ^/qhandler/(.*)/(.*) /qhandler.php?action=$1&hash=$2; - } - - location /edit { - rewrite ^/edit/(.*)/(.*) /edit.php?$1=$2; - } - - location @strip-ext { - rewrite ^(.*)$ $1.php last; - } - - location ~ ^/api/v1/(.*)$ { - try_files $uri $uri/ /json_api.php?query=$1&$args; - } - - location ^~ /.well-known/acme-challenge/ { - allow all; - default_type "text/plain"; - } - - # If behind reverse proxy, forwards the correct IP - set_real_ip_from 10.0.0.0/8; - set_real_ip_from 172.16.0.0/12; - set_real_ip_from 192.168.0.0/16; - set_real_ip_from fc00::/7; - real_ip_header X-Forwarded-For; - real_ip_recursive on; - - rewrite ^/.well-known/caldav$ /SOGo/dav/ permanent; - rewrite ^/.well-known/carddav$ /SOGo/dav/ permanent; - - location ^~ /principals { - return 301 /SOGo/dav; - } - - location ^~ /inc/lib/ { - deny all; - return 403; - } - - location ~ \.php$ { - try_files $uri =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass phpfpm:9002; - fastcgi_index index.php; - include /etc/nginx/fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_read_timeout 3600; - fastcgi_send_timeout 3600; - } - - location /rspamd/ { - location /rspamd/auth { - # proxy_pass is not inherited - proxy_pass http://rspamd:11334/auth; - proxy_intercept_errors on; - proxy_set_header Host $http_host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Real-IP $remote_addr; - proxy_redirect off; - error_page 401 /_rspamderror.php; - } - proxy_pass http://rspamd:11334/; - proxy_set_header Host $http_host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Real-IP $remote_addr; - proxy_redirect off; - } - - location ~* ^/Autodiscover/Autodiscover.xml { - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass phpfpm:9002; - include /etc/nginx/fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - try_files /autodiscover.php =404; - } - - location ~* ^/Autodiscover/Autodiscover.json { - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass phpfpm:9002; - include /etc/nginx/fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - try_files /autodiscover-json.php =404; - } - - location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml { - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass phpfpm:9002; - include /etc/nginx/fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - try_files /autoconfig.php =404; - } - - location /sogo-auth-verify { - internal; - proxy_set_header X-Original-URI $request_uri; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $http_host; - proxy_set_header Content-Length ""; - proxy_pass http://127.0.0.1:65510/sogo-auth; - proxy_pass_request_body off; - } - - location ^~ /Microsoft-Server-ActiveSync { - include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf; - include /etc/nginx/conf.d/sogo_eas.active; - proxy_connect_timeout 75; - proxy_send_timeout 3600; - proxy_read_timeout 3600; - proxy_buffer_size 128k; - proxy_buffers 64 512k; - proxy_busy_buffers_size 512k; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - client_body_buffer_size 512k; - client_max_body_size 0; - } - - location ^~ /SOGo { - location ~* ^/SOGo/so/.*\.(xml|js|html|xhtml)$ { - include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf; - include /etc/nginx/conf.d/sogo.active; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header x-webobjects-server-protocol HTTP/1.0; - proxy_set_header x-webobjects-remote-host $remote_addr; - proxy_set_header x-webobjects-server-name $server_name; - proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host; - proxy_set_header x-webobjects-server-port $server_port; - proxy_hide_header Content-Type; - add_header Content-Type text/plain; - break; - } - include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf; - include /etc/nginx/conf.d/sogo.active; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header x-webobjects-server-protocol HTTP/1.0; - proxy_set_header x-webobjects-remote-host $remote_addr; - proxy_set_header x-webobjects-server-name $server_name; - proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host; - proxy_set_header x-webobjects-server-port $server_port; - proxy_buffer_size 128k; - proxy_buffers 64 512k; - proxy_busy_buffers_size 512k; - proxy_send_timeout 3600; - proxy_read_timeout 3600; - client_body_buffer_size 128k; - client_max_body_size 0; - break; - } - - location ~* /sogo$ { - return 301 $client_req_scheme://$http_host/SOGo; - } - - location /SOGo.woa/WebServerResources/ { - alias /usr/lib/GNUstep/SOGo/WebServerResources/; - } - - location /.woa/WebServerResources/ { - alias /usr/lib/GNUstep/SOGo/WebServerResources/; - } - - location /SOGo/WebServerResources/ { - alias /usr/lib/GNUstep/SOGo/WebServerResources/; - } - - location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) { - alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2; - } - - include /etc/nginx/conf.d/site.*.custom; - - error_page 502 @awaitingupstream; - - location @awaitingupstream { - rewrite ^(.*)$ /_status.502.html break; - } - - location ~ ^/cache/(.*)$ { - try_files $uri $uri/ /resource.php?file=$1; - } diff --git a/data/conf/nginx/includes/sogo_proxy_auth.conf b/data/conf/nginx/includes/sogo_proxy_auth.conf deleted file mode 100644 index 045b98adc..000000000 --- a/data/conf/nginx/includes/sogo_proxy_auth.conf +++ /dev/null @@ -1,8 +0,0 @@ -auth_request /sogo-auth-verify; -auth_request_set $user $upstream_http_x_user; -auth_request_set $auth $upstream_http_x_auth; -auth_request_set $auth_type $upstream_http_x_auth_type; -proxy_set_header x-webobjects-remote-user "$user"; -proxy_set_header Authorization "$auth"; -proxy_set_header x-webobjects-auth-type "$auth_type"; - diff --git a/data/conf/nginx/meta_exporter.conf b/data/conf/nginx/meta_exporter.conf deleted file mode 100644 index 74291b147..000000000 --- a/data/conf/nginx/meta_exporter.conf +++ /dev/null @@ -1,19 +0,0 @@ -server { - listen 9081; - index index.php index.html; - server_name _; - error_log /var/log/nginx/error.log; - access_log /var/log/nginx/access.log; - root /meta_exporter; - client_max_body_size 10M; - location ~ \.php$ { - client_max_body_size 10M; - try_files $uri =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass phpfpm:9001; - fastcgi_index pipe.php; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - } -} diff --git a/data/conf/nginx/nginx.conf.j2 b/data/conf/nginx/nginx.conf.j2 new file mode 100644 index 000000000..13444129f --- /dev/null +++ b/data/conf/nginx/nginx.conf.j2 @@ -0,0 +1,119 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + # map-size.conf: + map_hash_max_size 256; + map_hash_bucket_size 256; + + # site.conf: + proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g; + server_names_hash_max_size 512; + server_names_hash_bucket_size 128; + + map $http_x_forwarded_proto $client_req_scheme { + default $scheme; + https https; + } + + # Default + server { + listen 127.0.0.1:65510; # sogo-auth verify internal + listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; + listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; + listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; + listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; + http2 on; + + ssl_certificate /etc/ssl/mail/cert.pem; + ssl_certificate_key /etc/ssl/mail/key.pem; + + server_name {{ MAILCOW_HOSTNAME }} autodiscover.* autoconfig.* {{ ADDITIONAL_SERVER_NAMES }}; + + include /etc/nginx/includes/sites-default.conf; + } + + # rspamd dynmaps: + server { + listen 8081; + listen [::]:8081; + index index.php index.html; + server_name _; + error_log /var/log/nginx/error.log; + access_log /var/log/nginx/access.log; + root /dynmaps; + + location ~ \.php$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass {{ PHPFPMHOST }}:9001; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + } + } + + # rspamd meta_exporter: + server { + listen 9081; + index index.php index.html; + server_name _; + error_log /var/log/nginx/error.log; + access_log /var/log/nginx/access.log; + root /meta_exporter; + client_max_body_size 10M; + location ~ \.php$ { + client_max_body_size 10M; + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass {{ PHPFPMHOST }}:9001; + fastcgi_index pipe.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + } + } + + {% for cert in valid_cert_dirs %} + server { + listen {{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; + listen [::]:{{ HTTP_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%}; + listen {{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; + listen [::]:{{ HTTPS_PORT }}{% if NGINX_USE_PROXY_PROTOCOL %} proxy_protocol{%endif%} ssl; + http2 on; + + ssl_certificate {{ cert.cert_path }}cert.pem; + ssl_certificate_key {{ cert.cert_path }}key.pem; + + server_name {{ cert.domains }}; + + include /etc/nginx/includes/sites-default.conf; + } + {% endfor %} +} diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf deleted file mode 100644 index fb40de879..000000000 --- a/data/conf/nginx/site.conf +++ /dev/null @@ -1,10 +0,0 @@ -proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g; -server_names_hash_max_size 512; -server_names_hash_bucket_size 128; - -map $http_x_forwarded_proto $client_req_scheme { - default $scheme; - https https; -} - -include /etc/nginx/conf.d/sites.active; diff --git a/data/conf/nginx/sites-default.conf.j2 b/data/conf/nginx/sites-default.conf.j2 new file mode 100644 index 000000000..783723bfc --- /dev/null +++ b/data/conf/nginx/sites-default.conf.j2 @@ -0,0 +1,276 @@ +include /etc/nginx/mime.types; +charset utf-8; +override_charset on; + +server_tokens off; + +ssl_protocols TLSv1.2 TLSv1.3; +ssl_prefer_server_ciphers on; +ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; +ssl_ecdh_curve X25519:X448:secp384r1:secp256k1; +ssl_session_cache shared:SSL:50m; +ssl_session_timeout 1d; +ssl_session_tickets off; + +add_header Strict-Transport-Security "max-age=15768000;"; +add_header X-Content-Type-Options nosniff; +add_header X-XSS-Protection "1; mode=block"; +add_header X-Robots-Tag none; +add_header X-Download-Options noopen; +add_header X-Frame-Options "SAMEORIGIN" always; +add_header X-Permitted-Cross-Domain-Policies none; +add_header Referrer-Policy strict-origin; + +index index.php index.html; + +client_max_body_size 0; + +gzip on; +gzip_disable "msie6"; + +gzip_vary on; +gzip_proxied off; +gzip_comp_level 6; +gzip_buffers 16 8k; +gzip_http_version 1.1; +gzip_min_length 256; +gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon; + +location ~ ^/(fonts|js|css|img)/ { + expires max; + add_header Cache-Control public; +} + +error_log /var/log/nginx/error.log; +access_log /var/log/nginx/access.log; +fastcgi_hide_header X-Powered-By; +absolute_redirect off; +root /web; + +# If behind reverse proxy, forwards the correct IP +set_real_ip_from 10.0.0.0/8; +set_real_ip_from 172.16.0.0/12; +set_real_ip_from 192.168.0.0/16; +set_real_ip_from fc00::/7; +{% if not TRUSTED_NETWORK %} +real_ip_header X-Forwarded-For; +{% else %} +set_real_ip_from {{ TRUSTED_NETWORK }}; +real_ip_header proxy_protocol; +{% endif %} +real_ip_recursive on; + + +location @strip-ext { + rewrite ^(.*)$ $1.php last; +} + +location ^~ /inc/lib/ { + deny all; + return 403; +} + +location ^~ /.well-known/acme-challenge/ { + allow all; + default_type "text/plain"; +} + +rewrite ^/.well-known/caldav$ /SOGo/dav/ permanent; +rewrite ^/.well-known/carddav$ /SOGo/dav/ permanent; + + +location / { + try_files $uri $uri/ @strip-ext; +} + +location /qhandler { + rewrite ^/qhandler/(.*)/(.*) /qhandler.php?action=$1&hash=$2; +} + +location /edit { + rewrite ^/edit/(.*)/(.*) /edit.php?$1=$2; +} + +location ~ ^/api/v1/(.*)$ { + try_files $uri $uri/ /json_api.php?query=$1&$args; +} + +location ~ ^/cache/(.*)$ { + try_files $uri $uri/ /resource.php?file=$1; +} + +location ~ \.php$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass {{ PHPFPMHOST }}:9002; + fastcgi_index index.php; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_read_timeout 3600; + fastcgi_send_timeout 3600; +} + +location ~* ^/Autodiscover/Autodiscover.xml { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass {{ PHPFPMHOST }}:9002; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + try_files /autodiscover.php =404; +} + +location ~* ^/Autodiscover/Autodiscover.json { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass {{ PHPFPMHOST }}:9002; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + try_files /autodiscover-json.php =404; +} + +location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass {{ PHPFPMHOST }}:9002; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + try_files /autoconfig.php =404; +} + +{% if not SKIP_RSPAMD %} +location /rspamd/ { + proxy_pass http://{{ RSPAMDHOST }}:11334/; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%}; + proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%}; + proxy_redirect off; + proxy_intercept_errors on; + error_page 401 /_rspamderror.php; +} +{% endif %} + +{% if not SKIP_SOGO %} +location ^~ /principals { + return 301 /SOGo/dav; +} + +location /sogo-auth-verify { + internal; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header Content-Length ""; + proxy_pass http://127.0.0.1:65510/sogo-auth; + proxy_pass_request_body off; +} + +location ^~ /Microsoft-Server-ActiveSync { + auth_request /sogo-auth-verify; + auth_request_set $user $upstream_http_x_user; + auth_request_set $auth $upstream_http_x_auth; + auth_request_set $auth_type $upstream_http_x_auth_type; + proxy_set_header x-webobjects-remote-user "$user"; + proxy_set_header Authorization "$auth"; + proxy_set_header x-webobjects-auth-type "$auth_type"; + + proxy_pass http://{{ SOGOHOST }}:20000/SOGo/Microsoft-Server-ActiveSync; + + proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%}; + proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%}; + proxy_connect_timeout 75; + proxy_send_timeout 3600; + proxy_read_timeout 3600; + proxy_buffer_size 128k; + proxy_buffers 64 512k; + proxy_busy_buffers_size 512k; + proxy_set_header Host $http_host; + client_body_buffer_size 512k; + client_max_body_size 0; +} + +location ^~ /SOGo { + location ~* ^/SOGo/so/.*\.(xml|js|html|xhtml)$ { + auth_request /sogo-auth-verify; + auth_request_set $user $upstream_http_x_user; + auth_request_set $auth $upstream_http_x_auth; + auth_request_set $auth_type $upstream_http_x_auth_type; + proxy_set_header x-webobjects-remote-user "$user"; + proxy_set_header Authorization "$auth"; + proxy_set_header x-webobjects-auth-type "$auth_type"; + + proxy_pass http://{{ SOGOHOST }}:20000; + + proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%}; + proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%}; + proxy_set_header Host $http_host; + proxy_set_header x-webobjects-server-protocol HTTP/1.0; + proxy_set_header x-webobjects-remote-host $remote_addr; + proxy_set_header x-webobjects-server-name $server_name; + proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host; + proxy_set_header x-webobjects-server-port $server_port; + proxy_hide_header Content-Type; + add_header Content-Type text/plain; + break; + } + auth_request /sogo-auth-verify; + auth_request_set $user $upstream_http_x_user; + auth_request_set $auth $upstream_http_x_auth; + auth_request_set $auth_type $upstream_http_x_auth_type; + proxy_set_header x-webobjects-remote-user "$user"; + proxy_set_header Authorization "$auth"; + proxy_set_header x-webobjects-auth-type "$auth_type"; + + proxy_pass http://{{ SOGOHOST }}:20000; + + proxy_set_header X-Forwarded-For {% if not NGINX_USE_PROXY_PROTOCOL %}$proxy_add_x_forwarded_for{% else %}$proxy_protocol_addr{%endif%}; + proxy_set_header X-Real-IP {% if not NGINX_USE_PROXY_PROTOCOL %}$remote_addr{% else %}$proxy_protocol_addr{%endif%}; + proxy_set_header Host $http_host; + proxy_set_header x-webobjects-server-protocol HTTP/1.0; + proxy_set_header x-webobjects-remote-host $remote_addr; + proxy_set_header x-webobjects-server-name $server_name; + proxy_set_header x-webobjects-server-url $client_req_scheme://$http_host; + proxy_set_header x-webobjects-server-port $server_port; + proxy_buffer_size 128k; + proxy_buffers 64 512k; + proxy_busy_buffers_size 512k; + proxy_send_timeout 3600; + proxy_read_timeout 3600; + client_body_buffer_size 128k; + client_max_body_size 0; + break; +} + +location ~* /sogo$ { + return 301 $client_req_scheme://$http_host/SOGo; +} + +location /SOGo.woa/WebServerResources/ { + alias /usr/lib/GNUstep/SOGo/WebServerResources/; +} + +location /.woa/WebServerResources/ { + alias /usr/lib/GNUstep/SOGo/WebServerResources/; +} + +location /SOGo/WebServerResources/ { + alias /usr/lib/GNUstep/SOGo/WebServerResources/; +} + +location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) { + alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2; +} +{% endif %} + + +include /etc/nginx/conf.d/site.*.custom; + +error_page 502 @awaitingupstream; + +location @awaitingupstream { + rewrite ^(.*)$ /_status.502.html break; +} + +location ~* \.php$ { + return 404; +} +location ~* \.twig$ { + return 404; +} diff --git a/data/conf/nginx/templates/listen_plain.template b/data/conf/nginx/templates/listen_plain.template deleted file mode 100644 index a044b22f2..000000000 --- a/data/conf/nginx/templates/listen_plain.template +++ /dev/null @@ -1,2 +0,0 @@ -listen ${HTTP_PORT}; -listen [::]:${HTTP_PORT}; diff --git a/data/conf/nginx/templates/listen_ssl.template b/data/conf/nginx/templates/listen_ssl.template deleted file mode 100644 index 40c402d04..000000000 --- a/data/conf/nginx/templates/listen_ssl.template +++ /dev/null @@ -1,3 +0,0 @@ -listen ${HTTPS_PORT} ssl; -listen [::]:${HTTPS_PORT} ssl; -http2 on; diff --git a/data/conf/nginx/templates/server_name.template.sh b/data/conf/nginx/templates/server_name.template.sh deleted file mode 100755 index 291b378fb..000000000 --- a/data/conf/nginx/templates/server_name.template.sh +++ /dev/null @@ -1 +0,0 @@ -echo "server_name ${MAILCOW_HOSTNAME} autodiscover.* autoconfig.* $(echo ${ADDITIONAL_SERVER_NAMES} | tr ',' ' ');" diff --git a/data/conf/nginx/templates/sites.template.sh b/data/conf/nginx/templates/sites.template.sh deleted file mode 100644 index 782c8141b..000000000 --- a/data/conf/nginx/templates/sites.template.sh +++ /dev/null @@ -1,38 +0,0 @@ -echo ' -server { - listen 127.0.0.1:65510; - include /etc/nginx/conf.d/listen_plain.active; - include /etc/nginx/conf.d/listen_ssl.active; - - ssl_certificate /etc/ssl/mail/cert.pem; - ssl_certificate_key /etc/ssl/mail/key.pem; - - include /etc/nginx/conf.d/server_name.active; - - include /etc/nginx/conf.d/includes/site-defaults.conf; -} -'; -for cert_dir in /etc/ssl/mail/*/ ; do - if [[ ! -f ${cert_dir}domains ]] || [[ ! -f ${cert_dir}cert.pem ]] || [[ ! -f ${cert_dir}key.pem ]]; then - continue - fi - # do not create vhost for default-certificate. the cert is already in the default server listen - domains="$(cat ${cert_dir}domains | sed -e 's/^[[:space:]]*//')" - case "${domains}" in - "") continue;; - "${MAILCOW_HOSTNAME}"*) continue;; - esac - echo -n ' -server { - include /etc/nginx/conf.d/listen_ssl.active; - - ssl_certificate '${cert_dir}'cert.pem; - ssl_certificate_key '${cert_dir}'key.pem; -'; - echo -n ' - server_name '${domains}'; - - include /etc/nginx/conf.d/includes/site-defaults.conf; -} -'; -done diff --git a/data/conf/nginx/templates/sogo.template b/data/conf/nginx/templates/sogo.template deleted file mode 100644 index 2c084389f..000000000 --- a/data/conf/nginx/templates/sogo.template +++ /dev/null @@ -1 +0,0 @@ -proxy_pass http://${IPV4_NETWORK}.248:20000; diff --git a/data/conf/nginx/templates/sogo_eas.template.sh b/data/conf/nginx/templates/sogo_eas.template.sh deleted file mode 100644 index b241ef0e7..000000000 --- a/data/conf/nginx/templates/sogo_eas.template.sh +++ /dev/null @@ -1,5 +0,0 @@ -if printf "%s\n" "${SKIP_SOGO}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then - echo "return 410;" -else - echo "proxy_pass http://${IPV4_NETWORK}.248:20000/SOGo/Microsoft-Server-ActiveSync;" -fi diff --git a/docker-compose.yml b/docker-compose.yml index b0324521a..31d6f56fd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -359,33 +359,26 @@ services: nginx-mailcow: depends_on: - - sogo-mailcow - - php-fpm-mailcow - redis-mailcow - image: nginx:mainline-alpine + - php-fpm-mailcow + - sogo-mailcow + - rspamd-mailcow + image: mailcow/nginx:1.00 dns: - ${IPV4_NETWORK:-172.22.1}.254 - command: /bin/sh -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active && - envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active && - envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active && - . /etc/nginx/conf.d/templates/server_name.template.sh > /etc/nginx/conf.d/server_name.active && - . /etc/nginx/conf.d/templates/sites.template.sh > /etc/nginx/conf.d/sites.active && - . /etc/nginx/conf.d/templates/sogo_eas.template.sh > /etc/nginx/conf.d/sogo_eas.active && - nginx -qt && - until ping phpfpm -c1 > /dev/null; do sleep 1; done && - until ping sogo -c1 > /dev/null; do sleep 1; done && - until ping redis -c1 > /dev/null; do sleep 1; done && - until ping rspamd -c1 > /dev/null; do sleep 1; done && - exec nginx -g 'daemon off;'" environment: - HTTPS_PORT=${HTTPS_PORT:-443} - HTTP_PORT=${HTTP_PORT:-80} - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} - - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} + - ADDITIONAL_SERVER_NAMES=${ADDITIONAL_SERVER_NAMES:-} - TZ=${TZ} - SKIP_SOGO=${SKIP_SOGO:-n} - - ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n} - - ADDITIONAL_SERVER_NAMES=${ADDITIONAL_SERVER_NAMES:-} + - SKIP_RSPAMD=${SKIP_RSPAMD:-n} + - PHPFPMHOST=${PHPFPMHOST:-php-fpm-mailcow} + - SOGOHOST=${SOGOHOST:-sogo-mailcow} + - RSPAMDHOST=${RSPAMDHOST:-rspamd-mailcow} + - REDISHOST=${REDISHOST:-redis-mailcow} + - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} volumes: - ./data/web:/web:ro,z - ./data/conf/rspamd/dynmaps:/dynmaps:ro,z From bca4e1a03d7a36b81a7e0b2399db3bbe932dc88c Mon Sep 17 00:00:00 2001 From: Niklas Meyer Date: Mon, 11 Nov 2024 16:50:14 +0100 Subject: [PATCH 11/23] update.sh: precaution ask for deletion of dns_blocklists.cf if old format (#6154) --- update.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update.sh b/update.sh index 3afe623c2..a388d1a25 100755 --- a/update.sh +++ b/update.sh @@ -299,7 +299,7 @@ fix_broken_dnslist_conf() { echo -e "\e[35mOk, not deleting it! Please make sure you take a look at postfix upon start then..." return 2 fi - fi + fi } From 852d944cfba4c06dcc3af44d698f68c25425a07c Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Thu, 4 Apr 2024 16:27:48 +0200 Subject: [PATCH 12/23] [Web] remove f2b banlist from json_api.php --- data/web/admin.php | 2 +- data/web/f2b-banlist.php | 11 +++++++++++ data/web/json_api.php | 14 -------------- data/web/templates/admin/tab-config-f2b.twig | 2 +- 4 files changed, 13 insertions(+), 16 deletions(-) create mode 100644 data/web/f2b-banlist.php diff --git a/data/web/admin.php b/data/web/admin.php index 5dd7b3c6b..f03e52e76 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -104,7 +104,7 @@ $template_data = [ 'all_domains' => $all_domains, 'mailboxes' => $mailboxes, 'f2b_data' => $f2b_data, - 'f2b_banlist_url' => getBaseUrl() . "/api/v1/get/fail2ban/banlist/" . $f2b_data['banlist_id'], + 'f2b_banlist_url' => getBaseUrl() . "/f2b-banlist?id=" . $f2b_data['banlist_id'], 'q_data' => quarantine('settings'), 'qn_data' => quota_notification('get'), 'pw_reset_data' => reset_password('get_notification'), diff --git a/data/web/f2b-banlist.php b/data/web/f2b-banlist.php new file mode 100644 index 000000000..05c769907 --- /dev/null +++ b/data/web/f2b-banlist.php @@ -0,0 +1,11 @@ +getChallenge(); return; break; - case "fail2ban": - if (!isset($_SESSION['mailcow_cc_role'])){ - switch ($object) { - case 'banlist': - header('Content-Type: text/plain'); - echo fail2ban('banlist', 'get', $extra); - break; - } - } - break; } if (isset($_SESSION['mailcow_cc_role'])) { switch ($category) { @@ -1420,10 +1410,6 @@ if (isset($_GET['query'])) { break; case "fail2ban": switch ($object) { - case 'banlist': - header('Content-Type: text/plain'); - echo fail2ban('banlist', 'get', $extra); - break; default: $data = fail2ban('get'); process_get_return($data); diff --git a/data/web/templates/admin/tab-config-f2b.twig b/data/web/templates/admin/tab-config-f2b.twig index bb4a2e85a..75c626641 100644 --- a/data/web/templates/admin/tab-config-f2b.twig +++ b/data/web/templates/admin/tab-config-f2b.twig @@ -99,7 +99,7 @@ {% endif %}
- + {% if is_https %} {% endif %} From 89fb1322c6f486a9883e6435a73537b13fbf3582 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Fri, 8 Nov 2024 10:53:22 +0100 Subject: [PATCH 13/23] Enable password protection for Redis --- data/Dockerfiles/acme/acme.sh | 4 +-- data/Dockerfiles/acme/obtain-certificate.sh | 2 +- data/Dockerfiles/dockerapi/main.py | 4 +-- data/Dockerfiles/dovecot/clean_q_aged.sh | 2 +- data/Dockerfiles/dovecot/docker-entrypoint.sh | 4 +-- data/Dockerfiles/dovecot/quarantine_notify.py | 2 +- data/Dockerfiles/dovecot/quota_notify.py | 2 +- data/Dockerfiles/dovecot/repl_health.sh | 4 +-- .../dovecot/syslog-ng-redis_slave.conf | 2 ++ data/Dockerfiles/dovecot/syslog-ng.conf | 2 ++ data/Dockerfiles/dovecot/trim_logs.sh | 4 +-- data/Dockerfiles/netfilter/main.py | 8 ++--- data/Dockerfiles/phpfpm/docker-entrypoint.sh | 4 +-- .../postfix/syslog-ng-redis_slave.conf | 2 ++ data/Dockerfiles/postfix/syslog-ng.conf | 2 ++ data/Dockerfiles/rspamd/docker-entrypoint.sh | 12 ++++---- .../sogo/syslog-ng-redis_slave.conf | 2 ++ data/Dockerfiles/sogo/syslog-ng.conf | 2 ++ data/Dockerfiles/watchdog/watchdog.sh | 20 ++++++------- data/conf/rspamd/dynmaps/aliasexp.php | 1 + data/conf/rspamd/dynmaps/forwardinghosts.php | 1 + data/conf/rspamd/meta_exporter/pipe.php | 7 +++-- data/conf/rspamd/meta_exporter/pipe_rl.php | 1 + data/conf/rspamd/meta_exporter/pushover.php | 1 + data/web/_rspamderror.php | 1 + data/web/autodiscover.php | 1 + data/web/inc/prerequisites.inc.php | 3 +- docker-compose.yml | 29 +++++++++++++------ generate_config.sh | 16 ++++++---- helper-scripts/_cold-standby.sh | 4 +-- helper-scripts/backup_and_restore.sh | 2 +- helper-scripts/nextcloud.sh | 4 +-- helper-scripts/reset-learns.sh | 10 +++---- update.sh | 9 ++++++ 34 files changed, 111 insertions(+), 63 deletions(-) diff --git a/data/Dockerfiles/acme/acme.sh b/data/Dockerfiles/acme/acme.sh index 3c7658d84..a63c1f199 100755 --- a/data/Dockerfiles/acme/acme.sh +++ b/data/Dockerfiles/acme/acme.sh @@ -4,9 +4,9 @@ exec 5>&1 # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - export REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}" + export REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" else - export REDIS_CMDLINE="redis-cli -h redis -p 6379" + export REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" fi until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do diff --git a/data/Dockerfiles/acme/obtain-certificate.sh b/data/Dockerfiles/acme/obtain-certificate.sh index 743441197..f9eb29d1f 100644 --- a/data/Dockerfiles/acme/obtain-certificate.sh +++ b/data/Dockerfiles/acme/obtain-certificate.sh @@ -124,7 +124,7 @@ case "$SUCCESS" in ;; *) # non-zero is non-fun log_f "Failed to obtain certificate ${CERT} for domains '${CERT_DOMAINS[*]}'" - redis-cli -h redis SET ACME_FAIL_TIME "$(date +%s)" + redis-cli -h redis -a ${REDISPASS} SET ACME_FAIL_TIME "$(date +%s)" exit 100${SUCCESS} ;; esac diff --git a/data/Dockerfiles/dockerapi/main.py b/data/Dockerfiles/dockerapi/main.py index 6f7a6042c..00c2ad5e3 100644 --- a/data/Dockerfiles/dockerapi/main.py +++ b/data/Dockerfiles/dockerapi/main.py @@ -34,9 +34,9 @@ async def lifespan(app: FastAPI): # Init redis client if os.environ['REDIS_SLAVEOF_IP'] != "": - redis_client = redis = await aioredis.from_url(f"redis://{os.environ['REDIS_SLAVEOF_IP']}:{os.environ['REDIS_SLAVEOF_PORT']}/0") + redis_client = redis = await aioredis.from_url(f"redis://{os.environ['REDIS_SLAVEOF_IP']}:{os.environ['REDIS_SLAVEOF_PORT']}/0", password=os.environ['REDISPASS']) else: - redis_client = redis = await aioredis.from_url("redis://redis-mailcow:6379/0") + redis_client = redis = await aioredis.from_url("redis://redis-mailcow:6379/0", password=os.environ['REDISPASS']) # Init docker clients sync_docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto') diff --git a/data/Dockerfiles/dovecot/clean_q_aged.sh b/data/Dockerfiles/dovecot/clean_q_aged.sh index ef6b61f1f..a43853646 100755 --- a/data/Dockerfiles/dovecot/clean_q_aged.sh +++ b/data/Dockerfiles/dovecot/clean_q_aged.sh @@ -2,7 +2,7 @@ source /source_env.sh -MAX_AGE=$(redis-cli --raw -h redis-mailcow GET Q_MAX_AGE) +MAX_AGE=$(redis-cli --raw -h redis-mailcow -a ${REDISPASS} GET Q_MAX_AGE) if [[ -z ${MAX_AGE} ]]; then echo "Max age for quarantine items not defined" diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index 55eed7956..72d560a5f 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -14,9 +14,9 @@ done # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}" + REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" else - REDIS_CMDLINE="redis-cli -h redis -p 6379" + REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" fi until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do diff --git a/data/Dockerfiles/dovecot/quarantine_notify.py b/data/Dockerfiles/dovecot/quarantine_notify.py index e8d743b31..dfcb1f2c6 100755 --- a/data/Dockerfiles/dovecot/quarantine_notify.py +++ b/data/Dockerfiles/dovecot/quarantine_notify.py @@ -31,7 +31,7 @@ try: while True: try: - r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0) + r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0, password=os.environ['REDISPASS']) r.ping() except Exception as ex: print('%s - trying again...' % (ex)) diff --git a/data/Dockerfiles/dovecot/quota_notify.py b/data/Dockerfiles/dovecot/quota_notify.py index 34b3e0ed9..c2c73e7a9 100755 --- a/data/Dockerfiles/dovecot/quota_notify.py +++ b/data/Dockerfiles/dovecot/quota_notify.py @@ -23,7 +23,7 @@ else: while True: try: - r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0) + r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0, password=os.environ['REDISPASS']) r.ping() except Exception as ex: print('%s - trying again...' % (ex)) diff --git a/data/Dockerfiles/dovecot/repl_health.sh b/data/Dockerfiles/dovecot/repl_health.sh index 93b66da49..447fbee5c 100755 --- a/data/Dockerfiles/dovecot/repl_health.sh +++ b/data/Dockerfiles/dovecot/repl_health.sh @@ -4,9 +4,9 @@ source /source_env.sh # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}" + REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" else - REDIS_CMDLINE="redis-cli -h redis -p 6379" + REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" fi # Is replication active? diff --git a/data/Dockerfiles/dovecot/syslog-ng-redis_slave.conf b/data/Dockerfiles/dovecot/syslog-ng-redis_slave.conf index 519928954..4b9bf287c 100644 --- a/data/Dockerfiles/dovecot/syslog-ng-redis_slave.conf +++ b/data/Dockerfiles/dovecot/syslog-ng-redis_slave.conf @@ -20,6 +20,7 @@ destination d_redis_ui_log { host("`REDIS_SLAVEOF_IP`") persist-name("redis1") port(`REDIS_SLAVEOF_PORT`) + auth("`REDISPASS`") command("LPUSH" "DOVECOT_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n") ); }; @@ -28,6 +29,7 @@ destination d_redis_f2b_channel { host("`REDIS_SLAVEOF_IP`") persist-name("redis2") port(`REDIS_SLAVEOF_PORT`) + auth("`REDISPASS`") command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)") ); }; diff --git a/data/Dockerfiles/dovecot/syslog-ng.conf b/data/Dockerfiles/dovecot/syslog-ng.conf index 3e929e7b9..c79eb92ee 100644 --- a/data/Dockerfiles/dovecot/syslog-ng.conf +++ b/data/Dockerfiles/dovecot/syslog-ng.conf @@ -20,6 +20,7 @@ destination d_redis_ui_log { host("redis-mailcow") persist-name("redis1") port(6379) + auth("`REDISPASS`") command("LPUSH" "DOVECOT_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n") ); }; @@ -28,6 +29,7 @@ destination d_redis_f2b_channel { host("redis-mailcow") persist-name("redis2") port(6379) + auth("`REDISPASS`") command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)") ); }; diff --git a/data/Dockerfiles/dovecot/trim_logs.sh b/data/Dockerfiles/dovecot/trim_logs.sh index 9b0824e1c..1055c985a 100755 --- a/data/Dockerfiles/dovecot/trim_logs.sh +++ b/data/Dockerfiles/dovecot/trim_logs.sh @@ -10,9 +10,9 @@ catch_non_zero() { source /source_env.sh # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}" + REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" else - REDIS_CMDLINE="redis-cli -h redis -p 6379" + REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" fi catch_non_zero "${REDIS_CMDLINE} LTRIM ACME_LOG 0 ${LOG_LINES}" catch_non_zero "${REDIS_CMDLINE} LTRIM POSTFIX_MAILLOG 0 ${LOG_LINES}" diff --git a/data/Dockerfiles/netfilter/main.py b/data/Dockerfiles/netfilter/main.py index c5667dc5f..36304bf0c 100644 --- a/data/Dockerfiles/netfilter/main.py +++ b/data/Dockerfiles/netfilter/main.py @@ -106,7 +106,7 @@ def get_ip(address): ip = ip.ipv4_mapped if ip.is_private or ip.is_loopback: return False - + return ip def ban(address): @@ -434,9 +434,9 @@ if __name__ == '__main__': redis_slaveof_ip = os.getenv('REDIS_SLAVEOF_IP', '') redis_slaveof_port = os.getenv('REDIS_SLAVEOF_PORT', '') if "".__eq__(redis_slaveof_ip): - r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0) + r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0, password=os.environ['REDISPASS']) else: - r = redis.StrictRedis(host=redis_slaveof_ip, decode_responses=True, port=redis_slaveof_port, db=0) + r = redis.StrictRedis(host=redis_slaveof_ip, decode_responses=True, port=redis_slaveof_port, db=0, password=os.environ['REDISPASS']) r.ping() pubsub = r.pubsub() except Exception as ex: @@ -452,7 +452,7 @@ if __name__ == '__main__': # clear bans in redis r.delete('F2B_ACTIVE_BANS') r.delete('F2B_PERM_BANS') - + refreshF2boptions() watch_thread = Thread(target=watch) diff --git a/data/Dockerfiles/phpfpm/docker-entrypoint.sh b/data/Dockerfiles/phpfpm/docker-entrypoint.sh index 20e9a405c..c9ca6e454 100755 --- a/data/Dockerfiles/phpfpm/docker-entrypoint.sh +++ b/data/Dockerfiles/phpfpm/docker-entrypoint.sh @@ -16,7 +16,7 @@ else REDIS_HOST="redis" REDIS_PORT="6379" fi -REDIS_CMDLINE="redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT}" +REDIS_CMDLINE="redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT} -a ${REDISPASS}" until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do echo "Waiting for Redis..." @@ -26,7 +26,7 @@ done # Set redis session store echo -n ' session.save_handler = redis -session.save_path = "tcp://'${REDIS_HOST}':'${REDIS_PORT}'" +session.save_path = "tcp://'${REDIS_HOST}':'${REDIS_PORT}'?auth='${REDISPASS}'" ' > /usr/local/etc/php/conf.d/session_store.ini # Check mysql_upgrade (master and slave) diff --git a/data/Dockerfiles/postfix/syslog-ng-redis_slave.conf b/data/Dockerfiles/postfix/syslog-ng-redis_slave.conf index cb1d1aa04..8e15932a2 100644 --- a/data/Dockerfiles/postfix/syslog-ng-redis_slave.conf +++ b/data/Dockerfiles/postfix/syslog-ng-redis_slave.conf @@ -20,6 +20,7 @@ destination d_redis_ui_log { host("`REDIS_SLAVEOF_IP`") persist-name("redis1") port(`REDIS_SLAVEOF_PORT`) + auth("`REDISPASS`") command("LPUSH" "POSTFIX_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n") ); }; @@ -28,6 +29,7 @@ destination d_redis_f2b_channel { host("`REDIS_SLAVEOF_IP`") persist-name("redis2") port(`REDIS_SLAVEOF_PORT`) + auth("`REDISPASS`") command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)") ); }; diff --git a/data/Dockerfiles/postfix/syslog-ng.conf b/data/Dockerfiles/postfix/syslog-ng.conf index 0990f1c05..fc7d1aa0f 100644 --- a/data/Dockerfiles/postfix/syslog-ng.conf +++ b/data/Dockerfiles/postfix/syslog-ng.conf @@ -20,6 +20,7 @@ destination d_redis_ui_log { host("redis-mailcow") persist-name("redis1") port(6379) + auth("`REDISPASS`") command("LPUSH" "POSTFIX_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n") ); }; @@ -28,6 +29,7 @@ destination d_redis_f2b_channel { host("redis-mailcow") persist-name("redis2") port(6379) + auth("`REDISPASS`") command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)") ); }; diff --git a/data/Dockerfiles/rspamd/docker-entrypoint.sh b/data/Dockerfiles/rspamd/docker-entrypoint.sh index cf09ee48f..513ca70a4 100755 --- a/data/Dockerfiles/rspamd/docker-entrypoint.sh +++ b/data/Dockerfiles/rspamd/docker-entrypoint.sh @@ -56,27 +56,29 @@ if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then cat < /etc/rspamd/local.d/redis.conf read_servers = "redis:6379"; write_servers = "${REDIS_SLAVEOF_IP}:${REDIS_SLAVEOF_PORT}"; +password = "${REDISPASS}"; timeout = 10; EOF - until [[ $(redis-cli -h redis-mailcow PING) == "PONG" ]]; do + until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} PING) == "PONG" ]]; do echo "Waiting for Redis @redis-mailcow..." sleep 2 done - until [[ $(redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} PING) == "PONG" ]]; do + until [[ $(redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} PING) == "PONG" ]]; do echo "Waiting for Redis @${REDIS_SLAVEOF_IP}..." sleep 2 done - redis-cli -h redis-mailcow SLAVEOF ${REDIS_SLAVEOF_IP} ${REDIS_SLAVEOF_PORT} + redis-cli -h redis-mailcow -a ${REDISPASS} SLAVEOF ${REDIS_SLAVEOF_IP} ${REDIS_SLAVEOF_PORT} else cat < /etc/rspamd/local.d/redis.conf servers = "redis:6379"; +password = "${REDISPASS}"; timeout = 10; EOF - until [[ $(redis-cli -h redis-mailcow PING) == "PONG" ]]; do + until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} PING) == "PONG" ]]; do echo "Waiting for Redis slave..." sleep 2 done - redis-cli -h redis-mailcow SLAVEOF NO ONE + redis-cli -h redis-mailcow -a ${REDISPASS} SLAVEOF NO ONE fi # Provide additional lua modules diff --git a/data/Dockerfiles/sogo/syslog-ng-redis_slave.conf b/data/Dockerfiles/sogo/syslog-ng-redis_slave.conf index 7abfc4b59..675e4c67a 100644 --- a/data/Dockerfiles/sogo/syslog-ng-redis_slave.conf +++ b/data/Dockerfiles/sogo/syslog-ng-redis_slave.conf @@ -22,6 +22,7 @@ destination d_redis_ui_log { host("`REDIS_SLAVEOF_IP`") persist-name("redis1") port(`REDIS_SLAVEOF_PORT`) + auth("`REDISPASS`") command("LPUSH" "SOGO_LOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n") ); }; @@ -30,6 +31,7 @@ destination d_redis_f2b_channel { host("`REDIS_SLAVEOF_IP`") persist-name("redis2") port(`REDIS_SLAVEOF_PORT`) + auth("`REDISPASS`") command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)") ); }; diff --git a/data/Dockerfiles/sogo/syslog-ng.conf b/data/Dockerfiles/sogo/syslog-ng.conf index f16a2920a..8460f2f95 100644 --- a/data/Dockerfiles/sogo/syslog-ng.conf +++ b/data/Dockerfiles/sogo/syslog-ng.conf @@ -22,6 +22,7 @@ destination d_redis_ui_log { host("redis-mailcow") persist-name("redis1") port(6379) + auth("`REDISPASS`") command("LPUSH" "SOGO_LOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n") ); }; @@ -30,6 +31,7 @@ destination d_redis_f2b_channel { host("redis-mailcow") persist-name("redis2") port(6379) + auth("`REDISPASS`") command("PUBLISH" "F2B_CHANNEL" "$(sanitize $MESSAGE)") ); }; diff --git a/data/Dockerfiles/watchdog/watchdog.sh b/data/Dockerfiles/watchdog/watchdog.sh index 81d65d907..46d48da6d 100755 --- a/data/Dockerfiles/watchdog/watchdog.sh +++ b/data/Dockerfiles/watchdog/watchdog.sh @@ -40,9 +40,9 @@ done # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}" + REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" else - REDIS_CMDLINE="redis-cli -h redis -p 6379" + REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" fi until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do @@ -330,7 +330,7 @@ redis_checks() { touch /tmp/redis-mailcow; echo "$(tail -50 /tmp/redis-mailcow)" > /tmp/redis-mailcow host_ip=$(get_container_ip redis-mailcow) err_c_cur=${err_count} - /usr/lib/nagios/plugins/check_tcp -4 -H redis-mailcow -p 6379 -E -s "PING\n" -q "QUIT" -e "PONG" 2>> /tmp/redis-mailcow 1>&2; err_count=$(( ${err_count} + $? )) + /usr/lib/nagios/plugins/check_tcp -4 -H redis-mailcow -p 6379 -E -s "AUTH ${REDISPASS}\nPING\n" -q "QUIT" -e "PONG" 2>> /tmp/redis-mailcow 1>&2; err_count=$(( ${err_count} + $? )) [ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1 [ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} )) progress "Redis" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c} @@ -503,12 +503,12 @@ dovecot_repl_checks() { err_count=0 diff_c=0 THRESHOLD=${DOVECOT_REPL_THRESHOLD} - D_REPL_STATUS=$(redis-cli -h redis -r GET DOVECOT_REPL_HEALTH) + D_REPL_STATUS=$(redis-cli -h redis -a ${REDISPASS} -r GET DOVECOT_REPL_HEALTH) # Reduce error count by 2 after restarting an unhealthy container trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1 while [ ${err_count} -lt ${THRESHOLD} ]; do err_c_cur=${err_count} - D_REPL_STATUS=$(redis-cli --raw -h redis GET DOVECOT_REPL_HEALTH) + D_REPL_STATUS=$(redis-cli --raw -h redis -a ${REDISPASS} GET DOVECOT_REPL_HEALTH) if [[ "${D_REPL_STATUS}" != "1" ]]; then err_count=$(( ${err_count} + 1 )) fi @@ -578,19 +578,19 @@ ratelimit_checks() { err_count=0 diff_c=0 THRESHOLD=${RATELIMIT_THRESHOLD} - RL_LOG_STATUS=$(redis-cli -h redis LRANGE RL_LOG 0 0 | jq .qid) + RL_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} LRANGE RL_LOG 0 0 | jq .qid) # Reduce error count by 2 after restarting an unhealthy container trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1 while [ ${err_count} -lt ${THRESHOLD} ]; do err_c_cur=${err_count} RL_LOG_STATUS_PREV=${RL_LOG_STATUS} - RL_LOG_STATUS=$(redis-cli -h redis LRANGE RL_LOG 0 0 | jq .qid) + RL_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} LRANGE RL_LOG 0 0 | jq .qid) if [[ ${RL_LOG_STATUS_PREV} != ${RL_LOG_STATUS} ]]; then err_count=$(( ${err_count} + 1 )) echo 'Last 10 applied ratelimits (may overlap with previous reports).' > /tmp/ratelimit echo 'Full ratelimit buckets can be emptied by deleting the ratelimit hash from within mailcow UI (see /debug -> Protocols -> Ratelimit):' >> /tmp/ratelimit echo >> /tmp/ratelimit - redis-cli --raw -h redis LRANGE RL_LOG 0 10 | jq . >> /tmp/ratelimit + redis-cli --raw -h redis -a ${REDISPASS} LRANGE RL_LOG 0 10 | jq . >> /tmp/ratelimit fi [ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1 [ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} )) @@ -673,7 +673,7 @@ acme_checks() { err_count=0 diff_c=0 THRESHOLD=${ACME_THRESHOLD} - ACME_LOG_STATUS=$(redis-cli -h redis GET ACME_FAIL_TIME) + ACME_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} GET ACME_FAIL_TIME) if [[ -z "${ACME_LOG_STATUS}" ]]; then ${REDIS_CMDLINE} SET ACME_FAIL_TIME 0 ACME_LOG_STATUS=0 @@ -685,7 +685,7 @@ acme_checks() { ACME_LOG_STATUS_PREV=${ACME_LOG_STATUS} ACME_LC=0 until [[ ! -z ${ACME_LOG_STATUS} ]] || [ ${ACME_LC} -ge 3 ]; do - ACME_LOG_STATUS=$(redis-cli -h redis GET ACME_FAIL_TIME 2> /dev/null) + ACME_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} GET ACME_FAIL_TIME 2> /dev/null) sleep 3 ACME_LC=$((ACME_LC+1)) done diff --git a/data/conf/rspamd/dynmaps/aliasexp.php b/data/conf/rspamd/dynmaps/aliasexp.php index 947a02444..824037cf1 100644 --- a/data/conf/rspamd/dynmaps/aliasexp.php +++ b/data/conf/rspamd/dynmaps/aliasexp.php @@ -25,6 +25,7 @@ catch (PDOException $e) { // Init Redis $redis = new Redis(); $redis->connect('redis-mailcow', 6379); +$redis->auth(getenv("REDISPASS")); function parse_email($email) { if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false; diff --git a/data/conf/rspamd/dynmaps/forwardinghosts.php b/data/conf/rspamd/dynmaps/forwardinghosts.php index 10285b715..2186d7f26 100644 --- a/data/conf/rspamd/dynmaps/forwardinghosts.php +++ b/data/conf/rspamd/dynmaps/forwardinghosts.php @@ -4,6 +4,7 @@ ini_set('error_reporting', 0); $redis = new Redis(); $redis->connect('redis-mailcow', 6379); +$redis->auth(getenv("REDISPASS")); function in_net($addr, $net) { $net = explode('/', $net); diff --git a/data/conf/rspamd/meta_exporter/pipe.php b/data/conf/rspamd/meta_exporter/pipe.php index 1858ee668..4d8e2a132 100644 --- a/data/conf/rspamd/meta_exporter/pipe.php +++ b/data/conf/rspamd/meta_exporter/pipe.php @@ -24,6 +24,7 @@ catch (PDOException $e) { // Init Redis $redis = new Redis(); $redis->connect('redis-mailcow', 6379); +$redis->auth(getenv("REDISPASS")); // Functions function parse_email($email) { @@ -96,10 +97,10 @@ $rcpt_final_mailboxes = array(); foreach (json_decode($rcpts, true) as $rcpt) { // Remove tag $rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt); - + // Break rcpt into local part and domain part $parsed_rcpt = parse_email($rcpt); - + // Skip if not a mailcow handled domain try { if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) { @@ -243,7 +244,7 @@ foreach ($rcpt_final_mailboxes as $rcpt_final) { WHERE `rcpt` = :rcpt2 ORDER BY id DESC LIMIT :retention_size - ) x + ) x );'); $stmt->execute(array( ':rcpt' => $rcpt_final, diff --git a/data/conf/rspamd/meta_exporter/pipe_rl.php b/data/conf/rspamd/meta_exporter/pipe_rl.php index 5f7fd42c3..f9a21caf7 100644 --- a/data/conf/rspamd/meta_exporter/pipe_rl.php +++ b/data/conf/rspamd/meta_exporter/pipe_rl.php @@ -14,6 +14,7 @@ try { else { $redis->connect('redis-mailcow', 6379); } + $redis->auth(getenv("REDISPASS")); } catch (Exception $e) { exit; diff --git a/data/conf/rspamd/meta_exporter/pushover.php b/data/conf/rspamd/meta_exporter/pushover.php index f122b281a..af1b21ebc 100644 --- a/data/conf/rspamd/meta_exporter/pushover.php +++ b/data/conf/rspamd/meta_exporter/pushover.php @@ -24,6 +24,7 @@ catch (PDOException $e) { // Init Redis $redis = new Redis(); $redis->connect('redis-mailcow', 6379); +$redis->auth(getenv("REDISPASS")); // Functions function parse_email($email) { diff --git a/data/web/_rspamderror.php b/data/web/_rspamderror.php index 6bdfb3495..4976e0b5e 100644 --- a/data/web/_rspamderror.php +++ b/data/web/_rspamderror.php @@ -7,6 +7,7 @@ try { else { $redis->connect('redis-mailcow', 6379); } + $redis->auth(getenv("REDISPASS")); } catch (Exception $e) { exit; diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php index 992524b35..6e1634bb7 100644 --- a/data/web/autodiscover.php +++ b/data/web/autodiscover.php @@ -16,6 +16,7 @@ try { else { $redis->connect('redis-mailcow', 6379); } + $redis->auth(getenv("REDISPASS")); } catch (Exception $e) { exit; diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php index 9c5203e7f..d64313127 100644 --- a/data/web/inc/prerequisites.inc.php +++ b/data/web/inc/prerequisites.inc.php @@ -68,6 +68,7 @@ try { else { $redis->connect('redis-mailcow', 6379); } + $redis->auth(getenv("REDISPASS")); } catch (Exception $e) { // Stop when redis is not available @@ -321,7 +322,7 @@ $UI_TEXTS = customize('get', 'ui_texts'); if (file_exists('/web/css/themes/'.$UI_THEME.'-bootstrap.css')) $css_minifier->add('/web/css/themes/'.$UI_THEME.'-bootstrap.css'); else - $css_minifier->add('/web/css/themes/lumen-bootstrap.css'); + $css_minifier->add('/web/css/themes/lumen-bootstrap.css'); // minify css build files foreach ($css_dir as $css_file) { $css_minifier->add('/web/css/build/' . $css_file); diff --git a/docker-compose.yml b/docker-compose.yml index 31d6f56fd..ccb2a7271 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,6 +43,7 @@ services: redis-mailcow: image: redis:7-alpine + command: '--requirepass ${REDISPASS}' volumes: - redis-vol-1:/data/ restart: always @@ -52,6 +53,7 @@ services: - "${REDIS_PORT:-127.0.0.1:7654}:6379" environment: - TZ=${TZ} + - REDISPASS=${REDISPASS} sysctls: - net.core.somaxconn=4096 networks: @@ -80,7 +82,7 @@ services: - clamd rspamd-mailcow: - image: mailcow/rspamd:1.98 + image: mailcow/rspamd:1.99 stop_grace_period: 30s depends_on: - dovecot-mailcow @@ -91,6 +93,7 @@ services: - IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - REDISPASS=${REDISPASS} - SPAMHAUS_DQS_KEY=${SPAMHAUS_DQS_KEY:-} volumes: - ./data/hooks/rspamd:/hooks:Z @@ -112,7 +115,7 @@ services: - rspamd php-fpm-mailcow: - image: mailcow/phpfpm:1.91.1 + image: mailcow/phpfpm:1.92 command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" depends_on: - redis-mailcow @@ -139,6 +142,7 @@ services: environment: - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - REDISPASS=${REDISPASS} - LOG_LINES=${LOG_LINES:-9999} - TZ=${TZ} - DBNAME=${DBNAME} @@ -177,7 +181,7 @@ services: - phpfpm sogo-mailcow: - image: mailcow/sogo:1.127.1 + image: mailcow/sogo:1.128 environment: - DBNAME=${DBNAME} - DBUSER=${DBUSER} @@ -194,6 +198,7 @@ services: - MASTER=${MASTER:-y} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - REDISPASS=${REDISPASS} dns: - ${IPV4_NETWORK:-172.22.1}.254 volumes: @@ -224,7 +229,7 @@ services: - sogo dovecot-mailcow: - image: mailcow/dovecot:2.2 + image: mailcow/dovecot:2.21 depends_on: - mysql-mailcow - netfilter-mailcow @@ -266,6 +271,7 @@ services: - MASTER=${MASTER:-y} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - REDISPASS=${REDISPASS} - COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized} - FLATCURVE_EXPERIMENTAL=${FLATCURVE_EXPERIMENTAL:-n} ports: @@ -308,7 +314,7 @@ services: - dovecot postfix-mailcow: - image: mailcow/postfix:1.77 + image: mailcow/postfix:1.78 depends_on: mysql-mailcow: condition: service_started @@ -330,6 +336,7 @@ services: - DBPASS=${DBPASS} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - REDISPASS=${REDISPASS} - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} - SPAMHAUS_DQS_KEY=${SPAMHAUS_DQS_KEY:-} cap_add: @@ -401,7 +408,7 @@ services: condition: service_started unbound-mailcow: condition: service_healthy - image: mailcow/acme:1.90 + image: mailcow/acme:1.91 dns: - ${IPV4_NETWORK:-172.22.1}.254 environment: @@ -424,6 +431,7 @@ services: - TZ=${TZ} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - REDISPASS=${REDISPASS} - SNAT_TO_SOURCE=${SNAT_TO_SOURCE:-n} - SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n} volumes: @@ -438,7 +446,7 @@ services: - acme netfilter-mailcow: - image: mailcow/netfilter:1.59 + image: mailcow/netfilter:1.60 stop_grace_period: 30s restart: always privileged: true @@ -450,6 +458,7 @@ services: - SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - REDISPASS=${REDISPASS} - MAILCOW_REPLICA_IP=${MAILCOW_REPLICA_IP:-} - DISABLE_NETFILTER_ISOLATION_RULE=${DISABLE_NETFILTER_ISOLATION_RULE:-n} network_mode: "host" @@ -457,7 +466,7 @@ services: - /lib/modules:/lib/modules:ro watchdog-mailcow: - image: mailcow/watchdog:2.05 + image: mailcow/watchdog:2.06 dns: - ${IPV4_NETWORK:-172.22.1}.254 tmpfs: @@ -503,6 +512,7 @@ services: - HTTPS_PORT=${HTTPS_PORT:-443} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - REDISPASS=${REDISPASS} - EXTERNAL_CHECKS_THRESHOLD=${EXTERNAL_CHECKS_THRESHOLD:-1} - NGINX_THRESHOLD=${NGINX_THRESHOLD:-5} - UNBOUND_THRESHOLD=${UNBOUND_THRESHOLD:-5} @@ -528,7 +538,7 @@ services: - watchdog dockerapi-mailcow: - image: mailcow/dockerapi:2.09 + image: mailcow/dockerapi:2.10 security_opt: - label=disable restart: always @@ -539,6 +549,7 @@ services: - TZ=${TZ} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} + - REDISPASS=${REDISPASS} volumes: - /var/run/docker.sock:/var/run/docker.sock:ro networks: diff --git a/generate_config.sh b/generate_config.sh index f5a2a01b2..46a36a179 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -43,7 +43,7 @@ if docker compose > /dev/null 2>&1; then sleep 2 echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m" else - echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" + echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m" exit 1 fi @@ -56,14 +56,14 @@ elif docker-compose > /dev/null 2>&1; then sleep 2 echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m" else - echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" + echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" echo -e "\e[31mPlease update/install manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m" exit 1 fi fi else - echo -e "\e[31mCannot find Docker Compose.\e[0m" + echo -e "\e[31mCannot find Docker Compose.\e[0m" echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/install/\e[0m" exit 1 fi @@ -229,7 +229,7 @@ else echo -e "\033[31mCould not determine branch input..." echo -e "\033[31mExiting." exit 1 -fi +fi if [ ! -z "${MAILCOW_BRANCH}" ]; then git_branch=${MAILCOW_BRANCH} @@ -264,6 +264,12 @@ DBUSER=mailcow DBPASS=$(LC_ALL=C /dev/null | head -c 28) DBROOT=$(LC_ALL=C /dev/null | head -c 28) +# ------------------------------ +# REDIS configuration +# ------------------------------ + +REDISPASS=$(LC_ALL=C /dev/null | head -c 28) + # ------------------------------ # HTTP/S Bindings # ------------------------------ @@ -510,7 +516,7 @@ WEBAUTHN_ONLY_TRUSTED_VENDORS=n # Spamhaus Data Query Service Key # Optional: Leave empty for none -# Enter your key here if you are using a blocked ASN (OVH, AWS, Cloudflare e.g) for the unregistered Spamhaus Blocklist. +# Enter your key here if you are using a blocked ASN (OVH, AWS, Cloudflare e.g) for the unregistered Spamhaus Blocklist. # If empty, it will completely disable Spamhaus blocklists if it detects that you are running on a server using a blocked AS. # Otherwise it will work normally. SPAMHAUS_DQS_KEY= diff --git a/helper-scripts/_cold-standby.sh b/helper-scripts/_cold-standby.sh index ff0512e07..2fd1dcb67 100755 --- a/helper-scripts/_cold-standby.sh +++ b/helper-scripts/_cold-standby.sh @@ -150,7 +150,7 @@ else exit 1 fi - REMOTE_ARCH=$(ssh -o StrictHostKeyChecking=no -i "${REMOTE_SSH_KEY}" ${REMOTE_SSH_HOST} -p ${REMOTE_SSH_PORT} "uname -m") + REMOTE_ARCH=$(ssh -o StrictHostKeyChecking=no -i "${REMOTE_SSH_KEY}" ${REMOTE_SSH_HOST} -p ${REMOTE_SSH_PORT} "uname -m") } @@ -204,7 +204,7 @@ fi # Trigger a Redis save for a consistent Redis copy echo -ne "\033[1mRunning redis-cli save... \033[0m" -docker exec $(docker ps -qf name=redis-mailcow) redis-cli save +docker exec $(docker ps -qf name=redis-mailcow) redis-cli -a ${REDISPASS} save # Syncing volumes related to compose project # Same here: make sure destination exists diff --git a/helper-scripts/backup_and_restore.sh b/helper-scripts/backup_and_restore.sh index dc30d5ea1..f8deb590e 100755 --- a/helper-scripts/backup_and_restore.sh +++ b/helper-scripts/backup_and_restore.sh @@ -119,7 +119,7 @@ function backup() { ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_crypt.tar.gz /crypt ;;& redis|all) - docker exec $(docker ps -qf name=redis-mailcow) redis-cli save + docker exec $(docker ps -qf name=redis-mailcow) redis-cli -a ${REDISPASS} save docker run --name mailcow-backup --rm \ -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:ro,z \ diff --git a/helper-scripts/nextcloud.sh b/helper-scripts/nextcloud.sh index 2df3ca161..b05a3c93b 100755 --- a/helper-scripts/nextcloud.sh +++ b/helper-scripts/nextcloud.sh @@ -101,11 +101,11 @@ if [[ ${NC_PURGE} == "y" ]]; then echo -e "\033[33mNot purging anything...\033[0m" exit 1 fi - docker exec -it $(docker ps -f name=redis-mailcow -q) /bin/sh -c ' cat <> mailcow.conf echo 'DISABLE_NETFILTER_ISOLATION_RULE=n' >> mailcow.conf fi + elif [[ "${option}" == "REDISPASS" ]]; then + if ! grep -q "${option}" mailcow.conf; then + echo "Adding new option \"${option}\" to mailcow.conf" + echo -e '\n# ------------------------------' >> mailcow.conf + echo '# REDIS configuration' >> mailcow.conf + echo -e '# ------------------------------\n' >> mailcow.conf + echo "REDISPASS=$(LC_ALL=C /dev/null | head -c 28)" >> mailcow.conf + fi elif ! grep -q "${option}" mailcow.conf; then echo "Adding new option \"${option}\" to mailcow.conf" echo "${option}=n" >> mailcow.conf From c1903f121d079d5cd32d6ecc10747f767362d73b Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Wed, 13 Nov 2024 16:48:30 +0100 Subject: [PATCH 14/23] [Redis] set password via docker-entrypoint.sh --- data/conf/redis/docker-entrypoint.sh | 6 ++++++ docker-compose.yml | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100755 data/conf/redis/docker-entrypoint.sh diff --git a/data/conf/redis/docker-entrypoint.sh b/data/conf/redis/docker-entrypoint.sh new file mode 100755 index 000000000..00bdab1b6 --- /dev/null +++ b/data/conf/redis/docker-entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +cat < /redis.conf +requirepass $REDISPASS +EOF +exec redis-server /redis.conf diff --git a/docker-compose.yml b/docker-compose.yml index ccb2a7271..6d32da7d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,9 +43,10 @@ services: redis-mailcow: image: redis:7-alpine - command: '--requirepass ${REDISPASS}' + entrypoint: /docker-entrypoint.sh volumes: - redis-vol-1:/data/ + - ./data/conf/redis/docker-entrypoint.sh:/docker-entrypoint.sh:z restart: always depends_on: - netfilter-mailcow From b0de756a7c741c1fd2ff560783833620b6878573 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Tue, 19 Nov 2024 14:54:36 +0100 Subject: [PATCH 15/23] [Redis] Rename docker-entrypoint.sh to redis-conf.sh --- data/conf/redis/{docker-entrypoint.sh => redis-conf.sh} | 1 + docker-compose.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) rename data/conf/redis/{docker-entrypoint.sh => redis-conf.sh} (98%) diff --git a/data/conf/redis/docker-entrypoint.sh b/data/conf/redis/redis-conf.sh similarity index 98% rename from data/conf/redis/docker-entrypoint.sh rename to data/conf/redis/redis-conf.sh index 00bdab1b6..95d50a39a 100755 --- a/data/conf/redis/docker-entrypoint.sh +++ b/data/conf/redis/redis-conf.sh @@ -3,4 +3,5 @@ cat < /redis.conf requirepass $REDISPASS EOF + exec redis-server /redis.conf diff --git a/docker-compose.yml b/docker-compose.yml index 6d32da7d0..226c48ac8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,10 +43,10 @@ services: redis-mailcow: image: redis:7-alpine - entrypoint: /docker-entrypoint.sh + entrypoint: /redis-conf.sh volumes: - redis-vol-1:/data/ - - ./data/conf/redis/docker-entrypoint.sh:/docker-entrypoint.sh:z + - ./data/conf/redis/redis-conf.sh:/redis-conf.sh:z restart: always depends_on: - netfilter-mailcow From 6831f94fdb209fb4a558f5f81fb7db340c856bd2 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Tue, 19 Nov 2024 15:10:52 +0100 Subject: [PATCH 16/23] [Redis] redis-cli suppress auth warning --- data/Dockerfiles/acme/acme.sh | 4 ++-- data/Dockerfiles/acme/obtain-certificate.sh | 2 +- data/Dockerfiles/dovecot/clean_q_aged.sh | 2 +- data/Dockerfiles/dovecot/docker-entrypoint.sh | 4 ++-- data/Dockerfiles/dovecot/repl_health.sh | 4 ++-- data/Dockerfiles/dovecot/trim_logs.sh | 4 ++-- data/Dockerfiles/phpfpm/docker-entrypoint.sh | 2 +- data/Dockerfiles/rspamd/docker-entrypoint.sh | 10 +++++----- data/Dockerfiles/watchdog/watchdog.sh | 18 +++++++++--------- helper-scripts/_cold-standby.sh | 2 +- helper-scripts/backup_and_restore.sh | 2 +- helper-scripts/nextcloud.sh | 2 +- helper-scripts/reset-learns.sh | 10 +++++----- 13 files changed, 33 insertions(+), 33 deletions(-) diff --git a/data/Dockerfiles/acme/acme.sh b/data/Dockerfiles/acme/acme.sh index a63c1f199..64a4d1765 100755 --- a/data/Dockerfiles/acme/acme.sh +++ b/data/Dockerfiles/acme/acme.sh @@ -4,9 +4,9 @@ exec 5>&1 # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - export REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" + export REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning" else - export REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" + export REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning" fi until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do diff --git a/data/Dockerfiles/acme/obtain-certificate.sh b/data/Dockerfiles/acme/obtain-certificate.sh index f9eb29d1f..16c4e2588 100644 --- a/data/Dockerfiles/acme/obtain-certificate.sh +++ b/data/Dockerfiles/acme/obtain-certificate.sh @@ -124,7 +124,7 @@ case "$SUCCESS" in ;; *) # non-zero is non-fun log_f "Failed to obtain certificate ${CERT} for domains '${CERT_DOMAINS[*]}'" - redis-cli -h redis -a ${REDISPASS} SET ACME_FAIL_TIME "$(date +%s)" + redis-cli -h redis -a ${REDISPASS} --no-auth-warning SET ACME_FAIL_TIME "$(date +%s)" exit 100${SUCCESS} ;; esac diff --git a/data/Dockerfiles/dovecot/clean_q_aged.sh b/data/Dockerfiles/dovecot/clean_q_aged.sh index a43853646..3fa8a7ddb 100755 --- a/data/Dockerfiles/dovecot/clean_q_aged.sh +++ b/data/Dockerfiles/dovecot/clean_q_aged.sh @@ -2,7 +2,7 @@ source /source_env.sh -MAX_AGE=$(redis-cli --raw -h redis-mailcow -a ${REDISPASS} GET Q_MAX_AGE) +MAX_AGE=$(redis-cli --raw -h redis-mailcow -a ${REDISPASS} --no-auth-warning GET Q_MAX_AGE) if [[ -z ${MAX_AGE} ]]; then echo "Max age for quarantine items not defined" diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh index 72d560a5f..7c6f46c60 100755 --- a/data/Dockerfiles/dovecot/docker-entrypoint.sh +++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh @@ -14,9 +14,9 @@ done # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" + REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning" else - REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" + REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning" fi until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do diff --git a/data/Dockerfiles/dovecot/repl_health.sh b/data/Dockerfiles/dovecot/repl_health.sh index 447fbee5c..2d7674bdc 100755 --- a/data/Dockerfiles/dovecot/repl_health.sh +++ b/data/Dockerfiles/dovecot/repl_health.sh @@ -4,9 +4,9 @@ source /source_env.sh # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" + REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning" else - REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" + REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning" fi # Is replication active? diff --git a/data/Dockerfiles/dovecot/trim_logs.sh b/data/Dockerfiles/dovecot/trim_logs.sh index 1055c985a..fceaae564 100755 --- a/data/Dockerfiles/dovecot/trim_logs.sh +++ b/data/Dockerfiles/dovecot/trim_logs.sh @@ -10,9 +10,9 @@ catch_non_zero() { source /source_env.sh # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" + REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning" else - REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" + REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning" fi catch_non_zero "${REDIS_CMDLINE} LTRIM ACME_LOG 0 ${LOG_LINES}" catch_non_zero "${REDIS_CMDLINE} LTRIM POSTFIX_MAILLOG 0 ${LOG_LINES}" diff --git a/data/Dockerfiles/phpfpm/docker-entrypoint.sh b/data/Dockerfiles/phpfpm/docker-entrypoint.sh index c9ca6e454..e6510de7a 100755 --- a/data/Dockerfiles/phpfpm/docker-entrypoint.sh +++ b/data/Dockerfiles/phpfpm/docker-entrypoint.sh @@ -16,7 +16,7 @@ else REDIS_HOST="redis" REDIS_PORT="6379" fi -REDIS_CMDLINE="redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT} -a ${REDISPASS}" +REDIS_CMDLINE="redis-cli -h ${REDIS_HOST} -p ${REDIS_PORT} -a ${REDISPASS} --no-auth-warning" until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do echo "Waiting for Redis..." diff --git a/data/Dockerfiles/rspamd/docker-entrypoint.sh b/data/Dockerfiles/rspamd/docker-entrypoint.sh index 513ca70a4..cf44c3063 100755 --- a/data/Dockerfiles/rspamd/docker-entrypoint.sh +++ b/data/Dockerfiles/rspamd/docker-entrypoint.sh @@ -59,26 +59,26 @@ write_servers = "${REDIS_SLAVEOF_IP}:${REDIS_SLAVEOF_PORT}"; password = "${REDISPASS}"; timeout = 10; EOF - until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} PING) == "PONG" ]]; do + until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning PING) == "PONG" ]]; do echo "Waiting for Redis @redis-mailcow..." sleep 2 done - until [[ $(redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} PING) == "PONG" ]]; do + until [[ $(redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning PING) == "PONG" ]]; do echo "Waiting for Redis @${REDIS_SLAVEOF_IP}..." sleep 2 done - redis-cli -h redis-mailcow -a ${REDISPASS} SLAVEOF ${REDIS_SLAVEOF_IP} ${REDIS_SLAVEOF_PORT} + redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning SLAVEOF ${REDIS_SLAVEOF_IP} ${REDIS_SLAVEOF_PORT} else cat < /etc/rspamd/local.d/redis.conf servers = "redis:6379"; password = "${REDISPASS}"; timeout = 10; EOF - until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} PING) == "PONG" ]]; do + until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning PING) == "PONG" ]]; do echo "Waiting for Redis slave..." sleep 2 done - redis-cli -h redis-mailcow -a ${REDISPASS} SLAVEOF NO ONE + redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning SLAVEOF NO ONE fi # Provide additional lua modules diff --git a/data/Dockerfiles/watchdog/watchdog.sh b/data/Dockerfiles/watchdog/watchdog.sh index 46d48da6d..dac0335fb 100755 --- a/data/Dockerfiles/watchdog/watchdog.sh +++ b/data/Dockerfiles/watchdog/watchdog.sh @@ -40,9 +40,9 @@ done # Do not attempt to write to slave if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then - REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS}" + REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning" else - REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS}" + REDIS_CMDLINE="redis-cli -h redis -p 6379 -a ${REDISPASS} --no-auth-warning" fi until [[ $(${REDIS_CMDLINE} PING) == "PONG" ]]; do @@ -503,12 +503,12 @@ dovecot_repl_checks() { err_count=0 diff_c=0 THRESHOLD=${DOVECOT_REPL_THRESHOLD} - D_REPL_STATUS=$(redis-cli -h redis -a ${REDISPASS} -r GET DOVECOT_REPL_HEALTH) + D_REPL_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning -r GET DOVECOT_REPL_HEALTH) # Reduce error count by 2 after restarting an unhealthy container trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1 while [ ${err_count} -lt ${THRESHOLD} ]; do err_c_cur=${err_count} - D_REPL_STATUS=$(redis-cli --raw -h redis -a ${REDISPASS} GET DOVECOT_REPL_HEALTH) + D_REPL_STATUS=$(redis-cli --raw -h redis -a ${REDISPASS} --no-auth-warning GET DOVECOT_REPL_HEALTH) if [[ "${D_REPL_STATUS}" != "1" ]]; then err_count=$(( ${err_count} + 1 )) fi @@ -578,19 +578,19 @@ ratelimit_checks() { err_count=0 diff_c=0 THRESHOLD=${RATELIMIT_THRESHOLD} - RL_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} LRANGE RL_LOG 0 0 | jq .qid) + RL_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning LRANGE RL_LOG 0 0 | jq .qid) # Reduce error count by 2 after restarting an unhealthy container trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1 while [ ${err_count} -lt ${THRESHOLD} ]; do err_c_cur=${err_count} RL_LOG_STATUS_PREV=${RL_LOG_STATUS} - RL_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} LRANGE RL_LOG 0 0 | jq .qid) + RL_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning LRANGE RL_LOG 0 0 | jq .qid) if [[ ${RL_LOG_STATUS_PREV} != ${RL_LOG_STATUS} ]]; then err_count=$(( ${err_count} + 1 )) echo 'Last 10 applied ratelimits (may overlap with previous reports).' > /tmp/ratelimit echo 'Full ratelimit buckets can be emptied by deleting the ratelimit hash from within mailcow UI (see /debug -> Protocols -> Ratelimit):' >> /tmp/ratelimit echo >> /tmp/ratelimit - redis-cli --raw -h redis -a ${REDISPASS} LRANGE RL_LOG 0 10 | jq . >> /tmp/ratelimit + redis-cli --raw -h redis -a ${REDISPASS} --no-auth-warning LRANGE RL_LOG 0 10 | jq . >> /tmp/ratelimit fi [ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1 [ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} )) @@ -673,7 +673,7 @@ acme_checks() { err_count=0 diff_c=0 THRESHOLD=${ACME_THRESHOLD} - ACME_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} GET ACME_FAIL_TIME) + ACME_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning GET ACME_FAIL_TIME) if [[ -z "${ACME_LOG_STATUS}" ]]; then ${REDIS_CMDLINE} SET ACME_FAIL_TIME 0 ACME_LOG_STATUS=0 @@ -685,7 +685,7 @@ acme_checks() { ACME_LOG_STATUS_PREV=${ACME_LOG_STATUS} ACME_LC=0 until [[ ! -z ${ACME_LOG_STATUS} ]] || [ ${ACME_LC} -ge 3 ]; do - ACME_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} GET ACME_FAIL_TIME 2> /dev/null) + ACME_LOG_STATUS=$(redis-cli -h redis -a ${REDISPASS} --no-auth-warning GET ACME_FAIL_TIME 2> /dev/null) sleep 3 ACME_LC=$((ACME_LC+1)) done diff --git a/helper-scripts/_cold-standby.sh b/helper-scripts/_cold-standby.sh index 2fd1dcb67..815152735 100755 --- a/helper-scripts/_cold-standby.sh +++ b/helper-scripts/_cold-standby.sh @@ -204,7 +204,7 @@ fi # Trigger a Redis save for a consistent Redis copy echo -ne "\033[1mRunning redis-cli save... \033[0m" -docker exec $(docker ps -qf name=redis-mailcow) redis-cli -a ${REDISPASS} save +docker exec $(docker ps -qf name=redis-mailcow) redis-cli -a ${REDISPASS} --no-auth-warning save # Syncing volumes related to compose project # Same here: make sure destination exists diff --git a/helper-scripts/backup_and_restore.sh b/helper-scripts/backup_and_restore.sh index f8deb590e..581a84091 100755 --- a/helper-scripts/backup_and_restore.sh +++ b/helper-scripts/backup_and_restore.sh @@ -119,7 +119,7 @@ function backup() { ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_crypt.tar.gz /crypt ;;& redis|all) - docker exec $(docker ps -qf name=redis-mailcow) redis-cli -a ${REDISPASS} save + docker exec $(docker ps -qf name=redis-mailcow) redis-cli -a ${REDISPASS} --no-auth-warning save docker run --name mailcow-backup --rm \ -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:ro,z \ diff --git a/helper-scripts/nextcloud.sh b/helper-scripts/nextcloud.sh index b05a3c93b..12dab3ef2 100755 --- a/helper-scripts/nextcloud.sh +++ b/helper-scripts/nextcloud.sh @@ -101,7 +101,7 @@ if [[ ${NC_PURGE} == "y" ]]; then echo -e "\033[33mNot purging anything...\033[0m" exit 1 fi - docker exec -it $(docker ps -f name=redis-mailcow -q) /bin/sh -c "cat < Date: Wed, 20 Nov 2024 09:57:14 +0100 Subject: [PATCH 17/23] [Web] add missing translation for ratelimit in templates overview --- data/web/js/site/mailbox.js | 27 ++++++++------------------- data/web/mailbox.php | 1 + data/web/templates/mailbox.twig | 1 + 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/data/web/js/site/mailbox.js b/data/web/js/site/mailbox.js index af2862a37..dcde0d8a0 100644 --- a/data/web/js/site/mailbox.js +++ b/data/web/js/site/mailbox.js @@ -693,8 +693,8 @@ jQuery(function($){ } else if (item.attributes.rl_frame === "d"){ item.attributes.rl_frame = lang_rl.day; } - item.attributes.rl_value = escapeHtml(item.attributes.rl_value); - + item.attributes.rl_value = (!item.attributes.rl_value) ? "∞" : escapeHtml(item.attributes.rl_value); + item.attributes.ratelimit = item.attributes.rl_value + " " + item.attributes.rl_frame; if (item.template.toLowerCase() == "default"){ item.action = '
' + @@ -818,14 +818,8 @@ jQuery(function($){ } }, { - title: 'rl_frame', - data: 'attributes.rl_frame', - defaultContent: '', - class: 'none', - }, - { - title: 'rl_value', - data: 'attributes.rl_value', + title: lang_edit.ratelimit, + data: 'attributes.ratelimit', defaultContent: '', class: 'none', }, @@ -1183,7 +1177,8 @@ jQuery(function($){ } else if (item.attributes.rl_frame === "d"){ item.attributes.rl_frame = lang_rl.day; } - item.attributes.rl_value = escapeHtml(item.attributes.rl_value); + item.attributes.rl_value = (!item.attributes.rl_value) ? "∞" : escapeHtml(item.attributes.rl_value); + item.attributes.ratelimit = item.attributes.rl_value + " " + item.attributes.rl_frame; item.attributes.quota = humanFileSize(item.attributes.quota); @@ -1328,14 +1323,8 @@ jQuery(function($){ } }, { - title: "rl_frame", - data: 'attributes.rl_frame', - defaultContent: '', - class: 'none', - }, - { - title: 'rl_value', - data: 'attributes.rl_value', + title: lang_edit.ratelimit, + data: 'attributes.ratelimit', defaultContent: '', class: 'none', }, diff --git a/data/web/mailbox.php b/data/web/mailbox.php index 65c76f531..a84e32c47 100644 --- a/data/web/mailbox.php +++ b/data/web/mailbox.php @@ -41,6 +41,7 @@ $template_data = [ 'mailboxes' => $mailboxes, 'lang_mailbox' => json_encode($lang['mailbox']), 'lang_rl' => json_encode($lang['ratelimit']), + 'lang_edit' => json_encode($lang['edit']), 'lang_datatables' => json_encode($lang['datatables']), ]; diff --git a/data/web/templates/mailbox.twig b/data/web/templates/mailbox.twig index b61896d70..f0b1af464 100644 --- a/data/web/templates/mailbox.twig +++ b/data/web/templates/mailbox.twig @@ -68,6 +68,7 @@ var acl = '{{ acl_json|raw }}'; var lang = {{ lang_mailbox|raw }}; var lang_rl = {{ lang_rl|raw }}; + var lang_edit = {{ lang_edit|raw }}; var lang_datatables = {{ lang_datatables|raw }}; var csrf_token = '{{ csrf_token }}'; var pagination_size = Math.trunc('{{ pagination_size }}'); From d08b9aec32ca0d901e5ded54954ed20655aec4c9 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Wed, 20 Nov 2024 11:09:49 +0100 Subject: [PATCH 18/23] [Web] Add additional columns to _sogo_static_view --- data/web/inc/init_db.inc.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index 8c4951d57..3cfeb37df 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -3,7 +3,7 @@ function init_db_schema() { try { global $pdo; - $db_version = "29072024_1000"; + $db_version = "20112024_1105"; $stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); @@ -111,6 +111,10 @@ function init_db_schema() { "c_name" => "VARCHAR(255) NOT NULL", "c_password" => "VARCHAR(255) NOT NULL DEFAULT ''", "c_cn" => "VARCHAR(255)", + "c_l" => "VARCHAR(255)", + "c_o" => "VARCHAR(255)", + "c_ou" => "VARCHAR(255)", + "c_telephonenumber" => "VARCHAR(255)", "mail" => "VARCHAR(255) NOT NULL", // TODO -> use TEXT and check if SOGo login breaks on empty aliases "aliases" => "TEXT NOT NULL", @@ -1004,7 +1008,7 @@ function init_db_schema() { ) ), "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" - ), + ), "pushover" => array( "cols" => array( "username" => "VARCHAR(255) NOT NULL", @@ -1388,7 +1392,7 @@ function init_db_schema() { "key_size" => 2048, "max_quota_for_domain" => 10240 * 1048576, ) - ); + ); $default_mailbox_template = array( "template" => "Default", "type" => "mailbox", @@ -1423,7 +1427,7 @@ function init_db_schema() { "acl_quarantine_category" => 1, "acl_app_passwds" => 1, ) - ); + ); $stmt = $pdo->prepare("SELECT id FROM `templates` WHERE `type` = :type AND `template` = :template"); $stmt->execute(array( ":type" => "domain", @@ -1437,8 +1441,8 @@ function init_db_schema() { ":type" => "domain", ":template" => $default_domain_template["template"], ":attributes" => json_encode($default_domain_template["attributes"]) - )); - } + )); + } $stmt = $pdo->prepare("SELECT id FROM `templates` WHERE `type` = :type AND `template` = :template"); $stmt->execute(array( ":type" => "mailbox", @@ -1452,8 +1456,8 @@ function init_db_schema() { ":type" => "mailbox", ":template" => $default_mailbox_template["template"], ":attributes" => json_encode($default_mailbox_template["attributes"]) - )); - } + )); + } if (php_sapi_name() == "cli") { echo "DB initialization completed" . PHP_EOL; From ba282233ea06823e827d85fad014ff68e23854b5 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Wed, 20 Nov 2024 13:05:02 +0100 Subject: [PATCH 19/23] [Web] allow dots in dkim selectors --- data/web/inc/functions.dkim.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/web/inc/functions.dkim.inc.php b/data/web/inc/functions.dkim.inc.php index 29b32d5d3..8b1766a20 100644 --- a/data/web/inc/functions.dkim.inc.php +++ b/data/web/inc/functions.dkim.inc.php @@ -26,7 +26,7 @@ function dkim($_action, $_data = null, $privkey = false) { ); continue; } - if (!ctype_alnum(str_replace(['-', '_'], '', $dkim_selector))) { + if (!ctype_alnum(str_replace(['-', '_', '.'], '', $dkim_selector))) { $_SESSION['return'][] = array( 'type' => 'danger', 'log' => array(__FUNCTION__, $_action, $_data), @@ -188,7 +188,7 @@ function dkim($_action, $_data = null, $privkey = false) { return false; } } - if (!ctype_alnum($dkim_selector)) { + if (!ctype_alnum(str_replace(['-', '_', '.'], '', $dkim_selector))) { $_SESSION['return'][] = array( 'type' => 'danger', 'log' => array(__FUNCTION__, $_action, $_data), From 4dbfd3abadf1a4cf75db19332cf22a25ce07169b Mon Sep 17 00:00:00 2001 From: Habetdin <15926758+Habetdin@users.noreply.github.com> Date: Mon, 25 Nov 2024 18:01:17 +0300 Subject: [PATCH 20/23] Update lang.ru-ru.json (#6184) --- data/web/lang/lang.ru-ru.json | 316 ++++++++++++++++++++++------------ 1 file changed, 204 insertions(+), 112 deletions(-) diff --git a/data/web/lang/lang.ru-ru.json b/data/web/lang/lang.ru-ru.json index 792d32661..ec86d7d06 100644 --- a/data/web/lang/lang.ru-ru.json +++ b/data/web/lang/lang.ru-ru.json @@ -14,6 +14,7 @@ "prohibited": "Запрещено правилами ACL", "protocol_access": "Настройка разрешенных протоколов", "pushover": "Pushover API", + "pw_reset": "Разрешить пользователям mailcow восстановление паролей", "quarantine": "Карантин - действия", "quarantine_attachments": "Карантин - вложения", "quarantine_category": "Категория уведомлений о спаме", @@ -28,8 +29,7 @@ "spam_score": "Политика фильтрации спама", "syncjobs": "Задания синхронизации", "tls_policy": "Политика шифрования", - "unlimited_quota": "Неограниченная квота для почтовых ящиков", - "pw_reset": "Разрешить сброс пароля пользователей mailcow" + "unlimited_quota": "Неограниченная квота для почтовых ящиков" }, "add": { "activate_filter_warn": "Активация этого фильтра отключит все остальные фильтры этого типа.", @@ -42,13 +42,14 @@ "alias_domain": "Псевдоним домена", "alias_domain_info": "Действительные имена доменов, раздёленные запятыми.", "app_name": "Название приложения", + "app_passwd_protocols": "Разрешенные протоколы для пароля приложения", "app_password": "Добавить пароль приложения", "automap": "Автоматическое слияние папок (\"Sent items\", \"Sent\" => \"Sent\" etc.)", "backup_mx_options": "Параметры резервного MX", "bcc_dest_format": "Место назначения BCC должно быть единственным действительным адресом электронной почты.
Если вам нужно отправить копию на несколько адресов - используйте псевдоним.", "comment_info": "Приватный комментарий не виден пользователям, а публичный - отображается рядом с псевдонимом в личном кабинете пользователя.", "custom_params": "Пользовательские параметры", - "custom_params_hint": "Верно: --param=xy, не верно: --param xy", + "custom_params_hint": "Верно: --param=xy, неверно: --param xy", "delete1": "Удаление из источника после завершения", "delete2": "Удаление писем по месту назначения, которые не находятся на исходном", "delete2duplicates": "Удаление дубликатов по назначению", @@ -58,6 +59,7 @@ "domain": "Домен", "domain_matches_hostname": "Домен %s соответствует имени хоста", "domain_quota_m": "Квота домена (MiB)", + "dry": "Имитировать синхронизацию", "enc_method": "Метод шифрования", "exclude": "Исключить объекты (regex)", "full_name": "Полное имя", @@ -89,7 +91,7 @@ "relay_all_info": "↪Если вы решите не ретранслировать всех получателей, вам нужно будет добавить (\"слепой\") почтовый адрес для каждого получателя, которого следует ретранслировать.", "relay_domain": "Ретрансляция этого домена", "relay_transport_info": "
Инфо
Вы можете настроить собственный транспорт для домена. Если такой настройки нет, то доставка будет выполнена на основе MX записей.", - "relay_unknown_only": "Ретрансляция только не существующих почтовых ящиков. Почта к существующим почтовым ящикам будут доставляться локально.", + "relay_unknown_only": "Ретрансляция только несуществующих почтовых ящиков. Почта существующих почтовых ящиков будут доставляться локально.", "relayhost_wrapped_tls_info": "Пожалуйста не используйте TLS порты (в основном это 465 порт).
\r\nИспользуйте любой не TLS порт который поддерживает STARTTLS. А для защиты от downgrate атак - настройке принудительную политику TLS.", "select": "Пожалуйста, выберите...", "select_domain": "Пожалуйста, сначала выберите домен", @@ -99,6 +101,7 @@ "subscribeall": "Подписаться на все папки и подпапки", "syncjob": "Добавить задание синхронизации", "syncjob_hint": "Пароли к вашему аккаунту будут сохранены на сервере в виде простого текста!", + "tags": "Теги", "target_address": "Владельцы псевдонима", "target_address_info": "Адреса почтовых ящиков, разделенные запятыми.", "target_domain": "Целевой домен", @@ -106,10 +109,7 @@ "timeout2": "Тайм-аут для подключения к локальному хосту", "username": "Имя пользователя", "validate": "Проверить", - "validation_success": "Проверка прошла успешно", - "tags": "Теги", - "app_passwd_protocols": "Разрешенные протоколы для пароля приложения", - "dry": "Имитировать синхронизацию" + "validation_success": "Проверка прошла успешно" }, "admin": { "access": "Настройки доступа", @@ -135,6 +135,8 @@ "admins": "Администраторы", "admins_ldap": "Администраторы LDAP", "advanced_settings": "Расширенные настройки", + "allowed_methods": "Access-Control-Allow-Methods", + "allowed_origins": "Access-Control-Allow-Origin", "api_allow_from": "Список IP-адресов для доступа к API (разделенных запятой или новой строкой)", "api_info": "API находится в стадии разработки. Документация находится по адресу /api", "api_key": "Ключ API", @@ -151,6 +153,8 @@ "change_logo": "Изменить логотип", "configuration": "Глобальные настройки", "convert_html_to_text": "Сконвертировать HTML в обычный текст", + "copy_to_clipboard": "Текст скопирован в буфер обмена!", + "cors_settings": "Настройки CORS", "credentials_transport_warning": "Предупреждение: добавление новой записи перезапишет учетные данные для всех записей с таким же следующим хостом.", "customer_id": "ID клиента", "customize": "Персонализация", @@ -179,10 +183,14 @@ "empty": "Пусто", "excludes": "Исключает этих получателей", "f2b_ban_time": "Время бана (в секундах)", + "f2b_ban_time_increment": "Время бана увеличивается с каждым баном", "f2b_blacklist": "Черный список подсетей/хостов", "f2b_filter": "Правила фильтрации с помощью регулярных выражений", "f2b_list_info": "Хосты или подсети, занесенные в черный список, всегда будут перевешивать объекты из белого списка. Обновление списка займет несколько секунд.", + "f2b_manage_external": "Внешнее управление Fail2Ban", + "f2b_manage_external_info": "Fail2ban по-прежнему будет вести банлист, но не будет активно устанавливать правила для блокировки трафика. Используйте сгенерированный ниже банлист для внешнего блокирования трафика.", "f2b_max_attempts": "Максимальное количество попыток", + "f2b_max_ban_time": "Максимальное время блокировки", "f2b_netban_ipv4": "Размер подсети IPv4 для применения бана (8-32)", "f2b_netban_ipv6": "Размер подсети IPv6 для применения бана (8-128)", "f2b_parameters": "Настройки Fail2ban", @@ -208,13 +216,18 @@ "include_exclude": "Включить/Исключить", "include_exclude_info": "По умолчанию - без выбора - все почтовые ящики адресованы", "includes": "Включить этих получателей", + "ip_check": "Проверить IP", + "ip_check_disabled": "Проверка IP отключена. Вы можете включить его в разделе
Система > Конфигурация > Параметры > Персонализация.", + "ip_check_opt_in": "Согласие на использование сторонних служб ipv4.mailcow.email и ipv6.mailcow.email для разрешения внешних IP-адресов.", "is_mx_based": "На основе MX", "last_applied": "Посл. применение", - "license_info": "Лицензия не обязательна, но её приобретение помогает дальнейшему развитию mailcow.
Зарегистрируйте свой GUID здесь или приобретите поддержку для вашей установки mailcow.", + "license_info": "Лицензия необязательна, но её приобретение помогает дальнейшему развитию mailcow.
Зарегистрируйте свой GUID здесь или приобретите поддержку для вашей установки mailcow.", "link": "Ссылка", "loading": "Пожалуйста, подождите...", "login_time": "Время входа", - "logo_info": "Ваше изображение будет масштабироваться до высоты 40px для верхней панели навигации и до 250px ширины для стартовой страницы.
Рекомендуется использовать векторную графику, на пример: .svg.", + "logo_dark_label": "Инвертированный для темного режима", + "logo_info": "Ваше изображение будет масштабироваться до высоты 40px для верхней панели навигации и до 250px ширины для стартовой страницы.
Рекомендуется использовать векторную графику, например: .svg.", + "logo_normal_label": "Обычный", "lookup_mx": "Назначение на основе резовинга MX записи по регулярному выражению (.*\\.example\\.com$ для маршрутизации всей почты через этот хост, если MX заканчивающийся на example.com)", "main_name": "Название для \"mailcow UI\"", "merged_vars_hint": "Серым цветом выделены строки полученные из vars.(local.)inc.php, они не могут быть изменены.", @@ -225,15 +238,16 @@ "no_active_bans": "В данный момент нет забаненных подсетей/хостов", "no_new_rows": "Нет доступных строк", "no_record": "Нет записей", - "oauth2_client_id": "ID клиента", - "oauth2_apps": "Приложения OAuth2", "oauth2_add_client": "Добавить клиента OAuth2", + "oauth2_apps": "Приложения OAuth2", + "oauth2_client_id": "ID клиента", "oauth2_client_secret": "Секретный ключ пользователя", "oauth2_info": "Реализация OAuth2 поддерживает предоставления кодов авторизации и выдает токены продления сессии.
\r\nСервер также автоматически выдает новый токен продления сессии, после того, как предыдущий был использован.

\r\n• Scope по умолчанию: profile. Только пользователи почтовых аккаунтов могут проходить аутентификацию через OAuth2. Если параметр области не указан, он возвращается к profile.
\r\n• Параметр state должен быть отправлен клиентом как часть запроса для авторизации.

\r\nПути для запросов OAuth2 API:
\r\n
    \r\n
  • Authorization endpoint: /oauth/authorize
  • \r\n
  • Token endpoint: /oauth/token
  • \r\n
  • Resource page: /oauth/profile
  • \r\n
\r\nГенерирование нового клиентского секрета не приводит к истечению существующих кодов авторизации, но они не смогут обновить свой токен.

\r\nОтзыв клиентских токенов приведет к немедленному прекращению всех активных сеансов. Все клиенты должны будут пройти повторную аутентификацию.", "oauth2_redirect_uri": "Переадресация URI", "oauth2_renew_secret": "Сгенерировать новый ключ клиента", "oauth2_revoke_tokens": "Отозвать все клиентские токены", "optional": "опционально", + "options": "Параметры", "password": "Пароль", "password_length": "Минимальная длина пароля", "password_policy": "Политика паролей", @@ -243,6 +257,11 @@ "password_policy_numbers": "Должен содержать цифру", "password_policy_special_chars": "Должны содержать специальный символ", "password_repeat": "Подтверждение пароля (повтор)", + "password_reset_info": "Если получатель не указан, использование данной функции недоступно.", + "password_reset_settings": "Параметры восстановления паролей", + "password_reset_tmpl_html": "Шаблон в виде HTML", + "password_reset_tmpl_text": "Шаблон в виде обычного текста", + "password_settings": "Параметры паролей", "priority": "Приоритет", "private_key": "Закрытый ключ", "quarantine": "Карантин", @@ -250,7 +269,7 @@ "quarantine_exclude_domains": "Исключить домены и псевдонимы доменов", "quarantine_max_age": "Максимальный период хранения в днях
Значение должно быть равно или больше 1 дня.", "quarantine_max_score": "Не уведомлять о спаме, если оценка письма выше, чем:
По умолчанию 9999.0", - "quarantine_max_size": "Максимальный размер в MiB (письма большего размера не будет сохранены):
0 означает, что карантин отключён.", + "quarantine_max_size": "Максимальный размер в MiB (письма большего размера не будут сохранены):
0 означает, что карантин отключён.", "quarantine_notification_html": "Шаблон уведомления:
Оставьте пустым, чтобы восстановить шаблон по умолчанию.", "quarantine_notification_sender": "Email-адрес для отправки уведомления", "quarantine_notification_subject": "Тема письма", @@ -259,6 +278,7 @@ "quarantine_release_format_att": "Как вложение", "quarantine_release_format_raw": "Оригинальное письмо", "quarantine_retention_size": "Количество писем, сохраняемых в карантине на аккаунт:
0 означает, что карантин отключён.", + "queue_unban": "разблокировать", "quota_notification_html": "Шаблон уведомления:
Оставьте пустым, чтобы восстановить шаблон по умолчанию.", "quota_notification_sender": "Email-адрес для отправки уведомления", "quota_notification_subject": "Тема письма", @@ -267,7 +287,7 @@ "quota_notifications_vars": "{{percent}} равно текущей квоте пользователя
{{username}} - имя почтового аккаунта", "r_active": "Включенные ограничения", "r_inactive": "Отключенные ограничения", - "r_info": "Не активные (серые) элементы списка ограничений - это не валидные ограничения, и они не могут быть перемещены.
Вы можете добавить новые элементы в inc/vars.local.inc.php чтобы иметь возможность настраивать их.", + "r_info": "Неактивные (серые) элементы списка ограничений - это некорректные ограничения, и они не могут быть перемещены.
Вы можете добавить новые элементы в inc/vars.local.inc.php чтобы иметь возможность настраивать их.", "rate_name": "Название очереди", "recipients": "Получатели", "refresh": "Обновить", @@ -282,6 +302,8 @@ "remove_row": "Удалить строку", "reset_default": "Восстановить по умолчанию", "reset_limit": "Удалить хэш", + "reset_password_vars": "{{link}} Сгенерированная ссылка для восстановление пароля
{{username}} Имя почтового ящика пользователя, запросившего восстановление пароля
{{username2}} Имя почтового ящика для восстановления
{{date}} Дата запроса на восстановление пароля
{{token_lifetime}} Срок действия токена в минутах
{{hostname}} Имя хоста mailcow", + "restore_template": "Оставьте пустым, чтобы восстановить шаблон по умолчанию.", "routing": "Маршрутизация", "rsetting_add_rule": "Добавить правило", "rsetting_content": "Содержание правила", @@ -290,14 +312,14 @@ "rsetting_none": "Нет доступных правил", "rsettings_insert_preset": "Вставить пример \"%s\"", "rsettings_preset_1": "Отключить все, кроме DKIM и ограничения скорости для аутентифицированных пользователей", - "rsettings_preset_2": "Не проверять письма на спам Postmaster", + "rsettings_preset_2": "Не проверять письма Postmaster на спам", "rsettings_preset_3": "Разрешить только определённых отправителей для почтового ящика (использование только в качестве внутреннего почтового ящика)", "rsettings_preset_4": "Отключить Rspamd для домена", "rspamd_com_settings": "Имена правил будут сгенерированы на основе их ID.
Инструкция доступна на сайте документация Rspamd user settings, заготовленные шаблоны:", "rspamd_global_filters": "Глобальные правила фильтрации", "rspamd_global_filters_agree": "Я понимаю, что я делаю, и буду осторожен!", "rspamd_global_filters_info": "Глобальные правила фильтрации содержат различные виды глобальных черных и белых списков.", - "rspamd_global_filters_regex": "Названия фильтров отражают их предназначение. Все правила должены состоять из регулярных выражений в формате \"/pattern/options\" (на пример: /.+@domain\\.tld/i).
\r\nНесмотря на то, что перед сохранением правил выполняется проверка регулярных выражений, функциональность Rspamds может быть нарушена, если будет использован
\r\n некорректный синтаксис. Будьте внимательны при написании правил.
Электронные письма от адресов электронной почты, проходящие по регулярным выражениям черных списков, будут отклонены без сохранения в карантин.
\r\n Rspamd попытается прочитать содержимое правил при их изменении. Но, если что, вы можете перезапустить Rspamd, чтобы принять последние изменения принудительно.", + "rspamd_global_filters_regex": "Названия фильтров отражают их предназначение. Все правила должены состоять из регулярных выражений в формате \"/pattern/options\" (например: /.+@domain\\.tld/i).
\r\nНесмотря на то, что перед сохранением правил выполняется проверка регулярных выражений, функциональность Rspamds может быть нарушена, если будет использован
\r\n некорректный синтаксис. Будьте внимательны при написании правил.
Электронные письма от адресов электронной почты, проходящие по регулярным выражениям черных списков, будут отклонены без сохранения в карантин.
\r\n Rspamd попытается прочитать содержимое правил при их изменении. Но, если что, вы можете перезапустить Rspamd, чтобы принять последние изменения принудительно.", "rspamd_settings_map": "Правила Rspamd", "sal_level": "Уровень Муу", "save": "Сохранить изменения", @@ -337,19 +359,7 @@ "username": "Имя пользователя", "validate_license_now": "Получить лицензию на основе GUID с сервера лицензий", "verify": "Проверить", - "yes": "✓", - "queue_unban": "разблокировать", - "f2b_ban_time_increment": "Время бана увеличивается с каждым баном", - "f2b_max_ban_time": "Максимальное время блокировки", - "allowed_origins": "Access-Control-Allow-Origin", - "cors_settings": "Настройки CORS", - "allowed_methods": "Access-Control-Allow-Methods", - "ip_check": "Проверить IP", - "ip_check_disabled": "Проверка IP отключена. Вы можете включить его в разделе
Система > Конфигурация > Параметры > Настроить.", - "ip_check_opt_in": "Согласие на использование сторонних служб ipv4.mailcow.email и ipv6.mailcow.email для разрешения внешних IP-адресов.", - "f2b_manage_external": "Внешнее управление Fail2Ban", - "f2b_manage_external_info": "Fail2ban по-прежнему будет вести банлист, но не будет активно устанавливать правила для блокировки трафика. Используйте сгенерированный ниже банлист для внешнего блокирования трафика.", - "copy_to_clipboard": "Текст скопирован в буфер обмена!" + "yes": "✓" }, "danger": { "access_denied": "Доступ запрещён, или указаны неверные данные", @@ -365,7 +375,10 @@ "bcc_exists": "Для типов %s уже существует карта BCC %s", "bcc_must_be_email": "Назначение BCC %s не является правильным адресом электронной почты", "comment_too_long": "Комментарий слишком длинный, придел 160 символов", + "cors_invalid_method": "Указан недопустимый метод разрешения", + "cors_invalid_origin": "Указан неверный Allow-Origin", "defquota_empty": "Квота по умолчанию не может быть 0.", + "demo_mode_enabled": "Демонстрационный режим включен", "description_invalid": "Недопустимое описание ресурса %s", "dkim_domain_or_sel_exists": "Ключ DKIM для \"%s\" уже существует", "dkim_domain_or_sel_invalid": "DKIM домен или селектор недопустимы для %s", @@ -375,20 +388,23 @@ "domain_not_empty": "Нельзя удалить непустой домен %s", "domain_not_found": "Домен %s не найден", "domain_quota_m_in_use": "Квота домена должна быть больше или равна %s MiB", - "extra_acl_invalid": "Адрес внешнего отправителя \"%s\" не валидный.", - "extra_acl_invalid_domain": "Адрес внешнего отправителя \"%s\" не валидный домен", + "extended_sender_acl_denied": "отсутствует ACL для установки внешних адресов отправителей", + "extra_acl_invalid": "Адрес внешнего отправителя \"%s\" некорректен", + "extra_acl_invalid_domain": "Адрес внешнего отправителя \"%s\" содержит некорректный домен", "fido2_verification_failed": "Ошибка валидации FIDO2: %s", "file_open_error": "Файл не может быть открыт на запись", "filter_type": "Неверный тип фильтра", "from_invalid": "Отправитель не может быть пустым", "global_filter_write_error": "Ошибка записи фильтра в файл: %s", - "global_map_invalid": "Идентификатор глобального правила %s не валидный", + "global_map_invalid": "Недопустимый идентификатор глобального правила %s", "global_map_write_error": "Не удалось создать глобальное правило ID %s: %s", - "goto_empty": "Псевдоним должен содержать по крайней мере один валидный адрес владельца", + "goto_empty": "Псевдоним должен содержать по крайней мере один действующий адрес владельца", "goto_invalid": "Недопустимый основной адрес %s", "ham_learn_error": "Ошибка при обучении полезной почты: %s", "imagick_exception": "Ошибка в Imagick при чтении изображения", + "img_dimensions_exceeded": "Разрешение изображения превышает допустимое значение", "img_invalid": "Невозможно проверить файл изображения", + "img_size_exceeded": "Изображение превышает допустимый размер файла", "img_tmp_missing": "Невозможно проверить файл изображения: временный файл не найден", "invalid_bcc_map_type": "Неверный тип правила BCC", "invalid_destination": "Назначение \"%s\" указано неверно", @@ -397,8 +413,9 @@ "invalid_mime_type": "Неверный mime type", "invalid_nexthop": "Формат следующего хоста неверен", "invalid_nexthop_authenticated": "Следующий хост существует с разными данными авторизации, пожалуйста, обновите существующие данные авторизации сначала для этого хоста.", - "invalid_recipient_map_new": "Новый получатель: %s не валидный", - "invalid_recipient_map_old": "Первоначальный получатель: %s не валидный", + "invalid_recipient_map_new": "Недопустимый новый получатель: %s", + "invalid_recipient_map_old": "Недопустимый исходный получатель: %s", + "invalid_reset_token": "Неверный токен восстановления", "ip_list_empty": "Список разрешенных IP адресов не может быть пустым", "is_alias": "%s уже известен как псевдоним адреса", "is_alias_or_mailbox": "%s уже известен как псевдоним или почтовый аккаунт", @@ -418,7 +435,7 @@ "max_quota_in_use": "Квота почтового аккаунта должна быть больше или равна %d MiB", "maxquota_empty": "Максимальная квота почтового аккаунта не должна быть 0.", "mysql_error": "Ошибка в MySQL: %s", - "network_host_invalid": "Сеть или хост: %s не валидный", + "network_host_invalid": "Недопустимые сеть или хост: %s", "next_hop_interferes": "%s пересекается с %s", "next_hop_interferes_any": "Существующий хост пересекается с %s", "nginx_reload_failed": "Обновление конфигурации Nginx не удалось: %s", @@ -428,6 +445,8 @@ "password_complexity": "Пароль не соответствует требованиям", "password_empty": "Пароль не может быть пустым", "password_mismatch": "Введенные пароли не совпадают", + "password_reset_invalid_user": "Почтовый ящик не найден или не задан адрес электронной почты для восстановления", + "password_reset_na": "Восстановление пароля в настоящее время недоступно. Пожалуйста, свяжитесь с вашим администратором.", "policy_list_from_exists": "Запись с указанным именем уже существует", "policy_list_from_invalid": "Запись имеет недопустимый формат", "private_key_error": "Ошибка приватного ключа: %s", @@ -436,17 +455,19 @@ "pushover_token": "Токен Pushover указан в неверном формате", "quota_not_0_not_numeric": "Размер квоты должен быть больше или равен нулю", "recipient_map_entry_exists": "Правило перезаписи \"%s\" уже существует", + "recovery_email_failed": "Не удалось отправить письмо для восстановления. Пожалуйста, свяжитесь с вашим администратором.", "redis_error": "Ошибка в Redis: %s", - "relayhost_invalid": "Правило %s не валидное", + "relayhost_invalid": "Недопустимое правило %s", "release_send_failed": "Сообщение не может быть восстановлено: %s", "reset_f2b_regex": "Сброс фильтров не был выполнен за отведённый промежуток времени, пожалуйста, повторите попытку или подождите еще несколько секунд и перезагрузите веб страницу.", + "reset_token_limit_exceeded": "Превышен лимит запросов на восстановление. Пожалуйста, попробуйте ещё раз позже.", "resource_invalid": "Недопустимое имя ресурса", "rl_timeframe": "Не верный временной интервал для лимита отправки", "rspamd_ui_pw_length": "Длина пароля должна составлять не менее 6 символов для Rspamd UI", "script_empty": "Скрипт не может быть пустым", "sender_acl_invalid": "Недопустимое значение ACL для: %s", "set_acl_failed": "Не удалось установить ACL", - "settings_map_invalid": "Правило ID: %s не валидное", + "settings_map_invalid": "Недопустимое правило ID %s", "sieve_error": "Ошибка в синтаксисе Sieve: %s", "spam_learn_error": "Ошибка при обучении спам фильтра: %s", "subject_empty": "Тема письма не может быть пустой", @@ -454,34 +475,57 @@ "targetd_not_found": "Основной домен %s не найден", "targetd_relay_domain": "Целевой домен %s уже является домен ретрансляции", "temp_error": "Временная ошибка", - "text_empty": "Текст не должен быть пустым", + "template_exists": "Шаблон %s уже существует", + "template_id_invalid": "Недопустимое значение ID шаблона: %s", + "template_name_invalid": "Недопустимое название шаблона", + "text_empty": "Текст не может быть пустым", "tfa_token_invalid": "Неправильный TFA токен", "tls_policy_map_dest_invalid": "Недопустимое значение назначения политики", "tls_policy_map_entry_exists": "Правило политики шифрования \"%s\" уже существует", "tls_policy_map_parameter_invalid": "Недопустимое значение параметра политики", + "to_invalid": "Получатель не может быть пустым", "totp_verification_failed": "Ошибка валидации TOTP", "transport_dest_exists": "Назначение для отправки \"%s\" уже существует", - "webauthn_verification_failed": "Ошибка валидации WebAuthn: %s", "unknown": "Произошла неизвестная ошибка", "unknown_tfa_method": "Неизвестный метод TFA", "unlimited_quota_acl": "Неограниченная квота запрещена политикой доступа", "username_invalid": "Имя пользователя %s нельзя использовать", "validity_missing": "Пожалуйста, назначьте срок действия", "value_missing": "Пожалуйста заполните все поля", - "yotp_verification_failed": "Ошибка валидации Yubico OTP: %s", - "cors_invalid_method": "Указан недопустимый метод разрешения", - "demo_mode_enabled": "Демонстрационный режим включен", - "cors_invalid_origin": "Указан неверный Allow-Origin" + "webauthn_authenticator_failed": "Выбранный аутентификатор не был найден", + "webauthn_publickey_failed": "Для выбранного аутентификатора не был сохранен открытый ключ", + "webauthn_username_failed": "Выбранный аутентификатор принадлежит другой учетной записи", + "webauthn_verification_failed": "Ошибка валидации WebAuthn: %s", + "yotp_verification_failed": "Ошибка валидации Yubico OTP: %s" + }, + "datatables": { + "collapse_all": "Свернуть все", + "decimal": ",", + "emptyTable": "В таблице отсутствуют данные", + "expand_all": "Развернуть все", + "info": "Показаны записи с _START_ по _END_ из _TOTAL_", + "infoEmpty": "Показано 0 записей", + "infoFiltered": "(отфильтровано из _MAX_ всех записей)", + "infoPostFix": "", + "lengthMenu": "Показать _MENU_ записей", + "loadingRecords": "Загрузка...", + "processing": "Пожалуйста, подождите...", + "search": "Поиск:", + "thousands": " ", + "zeroRecords": "Не найдено соответствующих записей" }, "debug": { + "architecture": "Архитектура", "chart_this_server": "Диаграмма (текущий сервер)", - "containers_info": "Статус контейнеров Docker", - "container_running": "Работающий", "container_disabled": "Контейнер остановлен или отключен", + "container_running": "Работающий", "container_stopped": "Остановлен", + "containers_info": "Статус контейнеров Docker", + "cores": "яд.", "current_time": "Системное время", "disk_usage": "Использование дискового пространства", "docs": "Проиндексировано объектов", + "error_show_ip": "Не удалось определить публичные IP-адреса", "external_logs": "Внешние журналы", "history_all_servers": "История (все серверы)", "in_memory_logs": "Журналы контейнеров", @@ -490,9 +534,12 @@ "log_info": "

Журналы контейнеров mailcow сохраняются в Redis, и раз в минуту строки журнала за пределами LOG_LINES (%d) удаляются, чтобы уменьшить нагрузку на сервер.\r\n
Сами журналы контейнеров не сохраняются после перезагрузки контейнера. Все контейнеры дополнительно пишут логи в службу Docker, и, следовательно, используют драйвер логирования по умолчанию. Журналы контейнеров предусмотрены только для отладки мелких проблем. Для других задач, пожалуйста, настройте драйвер логирования Docker самостоятельно.

\r\n

Внешние журналы собираются через API приложений.

\r\n

Статические журналы – это, в основном, журналы активности, которые не записываются в Dockerd, но все равно должны быть постоянными (за исключением журналов API).

", "login_time": "Время входа", "logs": "Журналы", + "memory": "Память", + "no_update_available": "Система обновлена до последней версии", "online_users": "Подключено пользователей", "restart_container": "Перезапустить", "service": "Сервис", + "show_ip": "Показать публичные IP-адреса", "size": "Индексы занимают", "solr_dead": "Solr не запущен. Если вы включили Solf в файле настроек mailcow.conf и это сообщение отображается более получаса, скорее всего Solr сломан.", "solr_status": "Состояние Solr", @@ -500,10 +547,13 @@ "started_on": "Запущен в", "static_logs": "Статические журналы", "success": "Успех", - "no_update_available": "Система обновлена до последней версии", "system_containers": "Система и контейнеры", + "timezone": "Часовой пояс", + "update_available": "Доступно обновление", + "update_failed": "Не удалось проверить наличие обновлений", "uptime": "Время работы", - "username": "Имя пользователя" + "username": "Имя пользователя", + "wip": "В настоящее время идёт разработка" }, "diagnostics": { "cname_from_a": "Значение, полученное из записи A/AAAA. Это поддерживается до тех пор, пока запись указывает на правильный ресурс.", @@ -514,7 +564,7 @@ "dns_records_name": "Название", "dns_records_status": "Статус", "dns_records_type": "Тип", - "optional": "Эта запись не обязательна." + "optional": "Эта запись необязательна." }, "edit": { "acl": "ACL (Список прав)", @@ -527,6 +577,7 @@ "allowed_protocols": "Разрешённые протоколы", "app_name": "Название приложения", "app_passwd": "Пароль приложения", + "app_passwd_protocols": "Разрешенные протоколы для пароля приложения", "automap": "Автоматическое слияние папок (\"Sent items\", \"Sent\" => \"Sent\" etc.)", "backup_mx_options": "Параметры резервного копирования MX", "bcc_dest_format": "Назначением для правила BCC должен быть единственный действительный адрес электронной почты.", @@ -534,6 +585,7 @@ "client_secret": "Секретный ключ пользователя", "comment_info": "Приватный комментарий не виден пользователям, а публичный - отображается рядом с псевдонимом в личном кабинете пользователя", "created_on": "Дата создания", + "custom_attributes": "Пользовательские атрибуты", "delete1": "Удаление из источника после завершения", "delete2": "Удаление писем по месту назначения, которые не находятся на исходном", "delete2duplicates": "Удаление дубликатов по назначению", @@ -542,6 +594,19 @@ "disable_login": "Вход в систему запрещен", "domain": "Изменение домена", "domain_admin": "Изменение администратора домена", + "domain_footer": "Нижний колонтитул домена", + "domain_footer_html": "HTML нижний колонтитул", + "domain_footer_info": "Нижние колонтитулы на уровне домена добавляются ко всем исходящим электронным письмам, связанным с адресом в этом домене.
Для нижнего колонтитула можно использовать следующие переменные:", + "domain_footer_info_vars": { + "auth_user": "{= auth_user =} - Аутентифицированное имя пользователя, указанное MTA", + "custom": "{= foo =} - Если почтовый ящик имеет пользовательский атрибут \"foo\" со значением \"bar\", он возвращает \"bar\".", + "from_addr": "{= from_addr =} - Из адресной части envelope", + "from_domain": "{= from_domain =} - из доменной части envelope", + "from_name": "{= from_name =} - Из названия envelope, например, для \"Mailcow <moo@mailcow.tld>\" возвращается \"Mailcow\"", + "from_user": "{= from_user =} - Из пользовательской части envelope, например, для \"moo@mailcow.tld\" возвращается \"moo\"" + }, + "domain_footer_plain": "ПРОСТОЙ нижний колонтитул", + "domain_footer_skip_replies": "Ignore footer on reply e-mails", "domain_quota": "Квота домена", "domains": "Домены", "dont_check_sender_acl": "Отключить проверку отправителя для домена %s и псевдонимов домена", @@ -550,9 +615,9 @@ "exclude": "Исключить объекты (regex)", "extended_sender_acl": "Внешние адреса почты", "extended_sender_acl_info": "Для внешних доменов должен быть импортирован или сгенерирован доменный ключ DKIM с соответствующей записью TXT в домене, если внешний домен использует DMARC.
\r\n Не забудьте добавить этот сервер к соответствующей записи SPF TXT внешнего домена.
\r\n Добавление домена из списка внешних адресов в mailcow автоматически удалит соответствующие записи из внешних адресов пользователей.
\r\n Чтобы разрешить пользователю отправку от имени *@domain.tld, укажите @domain.tld.", + "footer_exclude": "Исключить из нижнего колонтитула", "force_pw_update": "Требовать смены пароля при следующем входе в систему", "force_pw_update_info": "Пользователь должен будет войти в %s и сменить свой пароль. mailcow OAuth2, SOGo, EAS, IMAP/POP3 и SMTP будут не доступны до смены пароля.", - "footer_exclude": "Исключить из нижнего колонтитула", "full_name": "Полное имя", "gal": "GAL - Глобальная адресная книга", "gal_info": "GAL содержит все объекты домена и не подлежит редактированию. Информация о занятости в SOGo будет отсутствовать для домена, если данная функция будет отключена! Требуется перезапустить SOGo, чтобы применить изменения.", @@ -566,6 +631,11 @@ "mailbox": "Изменение почтового аккаунта", "mailbox_quota_def": "Квота по умолчанию", "mailbox_relayhost_info": "Применяется только к почтовому ящику и личным псевдонимам, вне зависимости от настроек маршрутизации на уровне домена.", + "mailbox_rename": "Переименовать почтовый ящик", + "mailbox_rename_agree": "Я сделал резервную копию.", + "mailbox_rename_alias": "Автоматически создать псевдоним", + "mailbox_rename_title": "Новое имя локального почтового ящика", + "mailbox_rename_warning": "ВАЖНО! Перед переименованием почтового ящика создайте резервную копию.", "max_aliases": "Максимум псевдонимов", "max_mailboxes": "Максимум почтовых ящиков", "max_quota": "Максимальная квота почтового аккаунта (MiB)", @@ -574,9 +644,10 @@ "mbox_rl_info": "Этот лимит применяется к SASL логину пользователя и соответствует любому адресу отправителя, используемому зарегистрированным пользователем. Лимит скорости почтового аккаунта перекрывает лимит скорости для всего домена.", "mins_interval": "Интервал (в минутах)", "multiple_bookings": "Несколько бронирований", - "none_inherit": "Отсутствует / Наследуется", "nexthop": "Следующий хост", + "none_inherit": "Отсутствует / Наследуется", "password": "Пароль", + "password_recovery_email": "Адрес для восстановления пароля", "password_repeat": "Подтверждение пароля (повтор)", "previous": "Предыдущая страница", "private_comment": "Приватный комментарий", @@ -587,6 +658,7 @@ "pushover_only_x_prio": "Получать уведомления только об письмах с высоким приоритетом [X-Priority: 1]", "pushover_sender_array": "Получать уведомления от списка адресов электронной почты (envelop-from разделенные запятыми):", "pushover_sender_regex": "Получать уведомления от отправителей, удовлетворяющих regex-выражению:", + "pushover_sound": "Звук уведомления", "pushover_text": "Текст уведомления", "pushover_title": "Заголовок уведомления", "pushover_vars": "Когда фильтрация по отправителю не определена, уведомления будут доставлятся от всех отправителей.
Можно использовать обычный фильтр по отправителю и расширенный regex-фильтр, а также оба сразу.
Пожалуйста, ознакомьтесь с Pushover Privacy Policy перед использованием шаблонов для текста и заголовка", @@ -600,7 +672,7 @@ "relay_all_info": "↪Если вы решите не ретранслировать всех получателей, вам нужно будет добавить (\"слепой\") почтовый аккаунт для каждого получателя, которого следует ретранслировать.", "relay_domain": "Ретрансляция этого домена", "relay_transport_info": "
Инфо
Вы можете настроить собственный транспорт для домена. Если такой настройки нет, то доставка будет выполнена на основе MX записей.", - "relay_unknown_only": "Ретрансляция только не существующих почтовых ящиков. Почта к существующим почтовым ящикам будут доставляться локально.", + "relay_unknown_only": "Ретрансляция только несуществующих почтовых ящиков. Почта к существующим почтовым ящикам будут доставляться локально.", "relayhost": "Маршрутизация на основе отправителя", "remove": "Удалить", "resource": "Ресурс", @@ -612,6 +684,8 @@ "sieve_desc": "Краткое описание", "sieve_type": "Тип фильтра", "skipcrossduplicates": "Пропускать повторяющиеся сообщения в папках", + "sogo_access": "Предоставить прямой доступ к SOGo", + "sogo_access_info": "Единый вход из интерфейса почты продолжает работать. Эта настройка не влияет на доступ ко всем другим службам, а также не удаляет или изменяет существующий профиль пользователя SOGo.", "sogo_visible": "Отображать псевдоним в SOGo", "sogo_visible_info": "Влияет только на объекты, которые могут отображаться в SOGo (персональные или общие псевдонимы, указывающие как минимум на один локальный почтовый аккаунт). Учтите, что если функция отключена, у пользователей не будет возможности выбрать адрес псевдонима в качестве отправителя в SOGo.", "spam_alias": "Создать или изменить временные (спам) псевдонимы", @@ -627,22 +701,7 @@ "title": "Изменение объекта", "unchanged_if_empty": "Если без изменений - оставьте пустым", "username": "Имя пользователя", - "validate_save": "Подтвердить и сохранить", - "sogo_access_info": "Единый вход из интерфейса почты продолжает работать. Эта настройка не влияет на доступ ко всем другим службам, а также не удаляет или изменяет существующий профиль пользователя SOGo.", - "app_passwd_protocols": "Разрешенные протоколы для пароля приложения", - "domain_footer_info": "Нижние колонтитулы на уровне домена добавляются ко всем исходящим электронным письмам, связанным с адресом в этом домене.
Для нижнего колонтитула можно использовать следующие переменные:", - "domain_footer_info_vars": { - "from_name": "{= from_name =} - Из названия envelope, например, для \"Mailcow <moo@mailcow.tld>\" возвращается \"Mailcow\"", - "auth_user": "{= auth_user =} - Аутентифицированное имя пользователя, указанное MTA", - "from_user": "{= from_user =} - Из пользовательской части envelope, например, для \"moo@mailcow.tld\" возвращается \"moo\"", - "from_addr": "{= from_addr =} - Из адресной части envelope", - "from_domain": "{= from_domain =} - из доменной части envelope", - "custom": "{= foo =} - Если почтовый ящик имеет пользовательский атрибут \"foo\" со значением \"bar\", он возвращает \"bar\"." - }, - "domain_footer": "Нижний колонтитул домена", - "domain_footer_html": "HTML нижний колонтитул", - "domain_footer_plain": "ПРОСТОЙ нижний колонтитул", - "custom_attributes": "Пользовательские атрибуты" + "validate_save": "Подтвердить и сохранить" }, "fido2": { "confirm": "Подтвердить", @@ -679,8 +738,8 @@ "apps": "Приложения", "debug": "Состояние сервера", "email": "E-Mail", - "mailcow_system": "Система", "mailcow_config": "Конфигурация", + "mailcow_system": "Система", "quarantine": "Карантин", "restart_netfilter": "Перезапустить netfilter", "restart_sogo": "Перезапустить SOGo", @@ -692,12 +751,19 @@ "session_expires": "Ваш сеанс закончится примерно через 15 секунд" }, "login": { + "back_to_mailcow": "Вернуться к mailcow", "delayed": "Вход был отложен на %s секунд.", "fido2_webauthn": "FIDO2/WebAuthn Login", + "forgot_password": "> Забыли пароль?", + "invalid_pass_reset_token": "Токен восстановления пароля недействителен или срок его действия истек.
Пожалуйста, запросите новую ссылку для восстановления пароля.", "login": "Войти", "mobileconfig_info": "Пожалуйста, войдите в систему как пользователь почтового аккаунта для загрузки профиля подключения Apple.", + "new_password": "Новый пароль", + "new_password_confirm": "Повторите новый пароль", "other_logins": "Вход с помощью ключа", "password": "Пароль", + "request_reset_password": "Запросить восстановление пароля", + "reset_password": "Восстановление пароля", "username": "Имя пользователя" }, "mailbox": { @@ -715,6 +781,7 @@ "add_mailbox": "Добавить почтовый аккаунт", "add_recipient_map_entry": "Добавить перезапись получателя", "add_resource": "Добавить ресурс", + "add_template": "Добавить шаблон", "add_tls_policy_map": "Добавить политику TLS", "address_rewriting": "Перезапись адресов", "alias": "Псевдоним", @@ -739,12 +806,12 @@ "bcc_to_rcpt": "Переключиться на тип \"получатель\"", "bcc_to_sender": "Переключиться на тип \"отправитель\"", "bcc_type": "Тип BCC", - "booking_null": "Всегда показывать как свободный", "booking_0_short": "Всегда свободнен", "booking_custom": "Лимит на количество бронирований", "booking_custom_short": "Жесткий лимит", - "booking_ltnull": "Неограниченный, занят при бронировании", "booking_lt0_short": "Неограниченный лимит", + "booking_ltnull": "Неограниченный, занят при бронировании", + "booking_null": "Всегда показывать как свободный", "catch_all": "Catch-all", "created_on": "Дата создания", "daily": "Раз в день", @@ -759,6 +826,7 @@ "domain_aliases": "Псевдонимы доменов", "domain_quota": "Квота", "domain_quota_total": "Квота домена", + "domain_templates": "Шаблоны доменов", "domains": "Домены", "edit": "Изменить", "empty": "Пусто", @@ -786,6 +854,7 @@ "mailbox_defaults_info": "Установите настройки по умолчанию для новых почтовых аккаунтов.", "mailbox_defquota": "Квота по умолчанию", "mailbox_quota": "Макс. квота почт. ящика", + "mailbox_templates": "Шаблоны почтовых ящиков", "mailboxes": "Почтовые ящики", "max_aliases": "Максимум псевдонимов", "max_mailboxes": "Максимум почтовых ящиков", @@ -813,16 +882,17 @@ "recipient_map_new": "Перезапись на", "recipient_map_new_info": "Должен быть действующим почтовым ящиком.", "recipient_map_old": "Получатель", - "recipient_map_old_info": "Должен быть валидный почтовым ящиком или доменом.", + "recipient_map_old_info": "Должен быть действующим почтовым ящиком или доменом.", "recipient_maps": "Перезапись получателя", "relay_all": "Ретрансляция всех получателей", + "relay_unknown": "Ретрансляция неизвестных получателей", "remove": "Удалить", "resources": "Ресурсы", "running": "В процессе", "sender": "Отправитель", "set_postfilter": "Использовать как постфильтр", "set_prefilter": "Использовать как предварительный фильтр", - "sieve_info": "Вы можете сохранить несколько фильтров для каждого пользователя, но только один предварительный фильтр и один постфильтр могут быть активными одновременно.
\r\n Каждый фильтр будет обработан в описанном порядке. Не сломанный скрипт, не keep; не остановит обработку дальнейших скриптов.

Global sieve prefilter • Prefilter • User scripts • Postfilter • Global sieve postfilter", + "sieve_info": "Вы можете сохранить несколько фильтров для каждого пользователя, но только один предварительный фильтр и один постфильтр могут быть активными одновременно.
\r\n Каждый фильтр будет обработан в описанном порядке. Ни сломанный скрипт, ни keep; не остановит обработку дальнейших скриптов.

Global sieve prefilter • Prefilter • User scripts • Postfilter • Global sieve postfilter", "sieve_preset_1": "Discard mail with probable dangerous file types", "sieve_preset_2": "Always mark the e-mail of a specific sender as seen", "sieve_preset_3": "Discard silently, stop all further sieve processing", @@ -839,25 +909,27 @@ "stats": "Статистика", "status": "Статус", "sync_jobs": "Задания синхронизации", + "syncjob_EXIT_AUTHENTICATION_FAILURE": "Ошибка авторизации", + "syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Неправильное имя пользователя или пароль", + "syncjob_EXIT_CONNECTION_FAILURE": "Ошибка связи с сервером", + "syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Не удалось подключиться к удаленному серверу", + "syncjob_EXIT_OVERQUOTA": "Целевой почтовый ящик превысил квоту", + "syncjob_EXIT_TLS_FAILURE": "Ошибка установки шифрованного соединения", + "syncjob_EX_OK": "Успешно", "syncjob_check_log": "Проверить журнал", "syncjob_last_run_result": "Результат последнего запуска", - "syncjob_EX_OK": "Успешно", - "syncjob_EXIT_CONNECTION_FAILURE": "Ошибка связи с сервером", - "syncjob_EXIT_TLS_FAILURE": "Ошибка установки шифрованного соединения", - "syncjob_EXIT_AUTHENTICATION_FAILURE": "Ошибка авторизации", - "syncjob_EXIT_OVERQUOTA": "Целевой почтовый ящик превысил квоту", - "syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Не удалось подключиться к удаленному серверу", - "syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Неправильное имя пользователя или пароль", "table_size": "Размер таблицы", "table_size_show_n": "Отображать %s полей", "target_address": "Владельцы псевдонима", "target_domain": "Целевой домен", + "template": "Шаблон", + "templates": "Шаблоны", "tls_enforce_in": "Принудительный TLS (входящие)", "tls_enforce_out": "Принудительный TLS (исходящие)", "tls_map_dest": "Назначение", "tls_map_dest_info": "пример: example.org, .example.org, [mail.example.org]:25", "tls_map_parameters": "Параметры", - "tls_map_parameters_info": "Оставьте поле пустым или укажите параметры, на пример: protocols=!SSLv2 ciphers=medium exclude=3DES", + "tls_map_parameters_info": "Оставьте поле пустым или укажите параметры, например: protocols=!SSLv2 ciphers=medium exclude=3DES", "tls_map_policy": "Политика", "tls_policy_maps": "Правила TLS", "tls_policy_maps_enforced_tls": "Для исходящих сообщений от пользователей с включенной принудительной политикой шифрования исходящих соединений не описанные глобальной политикой,
\r\n будут применены значения по умолчанию, указанные в smtp_tls_mandatory_protocols и smtp_tls_mandatory_ciphers.", @@ -932,14 +1004,26 @@ "type": "Тип" }, "queue": { - "queue_manager": "Очередь на отправку" + "ays": "Пожалуйста, подтвердите, что вы хотите удалить все элементы из текущей очереди.", + "delete": "Удалить все", + "deliver_mail": "Доставить", + "deliver_mail_legend": "Попытаться повторно доставить выбранные письма.", + "flush": "Обработать очередь", + "hold_mail": "Отложить", + "hold_mail_legend": "Удержать выбранные сообщения. (Предотвратить дальнейшие попытки доставки)", + "info": "Очередь отправки почты содержит все письма, которые ожидают доставки. Если письмо надолго задерживается в почтовой очереди, оно автоматически удаляется системой.
Сообщение об ошибке в соответствующих письмах содержит информацию о том, почему письмо не удалось доставить.", + "legend": "Описание действий с почтовой очередью:", + "queue_manager": "Очередь на отправку", + "show_message": "Показать сообщение", + "unhold_mail": "Высвободить", + "unhold_mail_legend": "Освобождает выбранные письма для доставки. (Требуется предварительное удержание)" }, "ratelimit": { + "day": "сообщений / день", "disabled": "Отключен", - "second": "сообщений / секунду", - "minute": "сообщений / минуту", "hour": "сообщений / час", - "day": "сообщений / день" + "minute": "сообщений / минуту", + "second": "сообщений / секунду" }, "start": { "help": "Справка", @@ -965,6 +1049,7 @@ "bcc_deleted": "Правила BCC удалены: %s", "bcc_edited": "Правило BCC %s отредактировано", "bcc_saved": "Правило BCC сохранено", + "cors_headers_edited": "Настройки CORS сохранены", "db_init_complete": "Инициализация базы данных завершена", "delete_filter": "Фильтр ID %s удалён", "delete_filters": "Фильтры удалены: %s", @@ -973,19 +1058,23 @@ "dkim_added": "DKIM ключ сохранён", "dkim_duplicated": "DKIM ключи для домена %s были скопированы в %s", "dkim_removed": "DKIM ключ %s удалён", + "domain_add_dkim_available": "DKIM ключ уже существует", "domain_added": "Добавлен домен %s", "domain_admin_added": "Администратор домена %s добавлен", "domain_admin_modified": "Сохранить изменения администратора домена %s", "domain_admin_removed": "Администратор домена %s удалён", + "domain_footer_modified": "Изменения в нижнем колонтитуле домена %s сохранены", "domain_modified": "Сохранить изменения домена %s", "domain_removed": "Домен %s удалён", "dovecot_restart_success": "Dovecot перезапущен успешно", "eas_reset": "Кеш ActiveSync для пользователя %s был сброшен", + "f2b_banlist_refreshed": "Идентификатор банлиста был успешно обновлен.", "f2b_modified": "Изменения параметров Fail2ban сохранены", "forwarding_host_added": "Перенаправление узла %s добавлено", "forwarding_host_removed": "Перенаправление узла %s удалено", "global_filter_written": "Фильтр успешно записан в файл", "hash_deleted": "Хеш удалён", + "ip_check_opt_in_modified": "Параметры проверки IP успешно обновлены", "item_deleted": "Обьект %s удалён", "item_released": "Письмо %s восстановлено из карантина", "items_deleted": "Обьекты %s удалены", @@ -996,14 +1085,17 @@ "mailbox_added": "Почтовый аккаунт %s добавлен", "mailbox_modified": "Изменения почтового аккаунта %s сохранены", "mailbox_removed": "Почтовый аккаунт %s удалён", + "mailbox_renamed": "Почтовый аккаунт %s был переименован в %s", "nginx_reloaded": "Обновление конфигурация Nginx закончено", "object_modified": "Изменения объекта %s сохранены", + "password_changed_success": "Пароль был успешно изменен", "password_policy_saved": "Политика паролей сохранена", "pushover_settings_edited": "Настройки сохранены, пожалуйста, выполните проверку доступа", "qlearn_spam": "Письмо ID %s было изучено как спам и удалено", "queue_command_success": "Команда выполнена успешно", "recipient_map_entry_deleted": "Правило перезаписи получателя ID %s было удалено", "recipient_map_entry_saved": "Правило перезаписи получателя \"%s\" было сохранено", + "recovery_email_sent": "Письмо для восстановления пароля отправлено на %s", "relayhost_added": "Промежуточный узел %s добавлен", "relayhost_removed": "Промежуточный узел %s удалён", "reset_main_logo": "Восстановить логотип по умолчанию", @@ -1016,6 +1108,9 @@ "settings_map_added": "Правило добавлено", "settings_map_removed": "Правило ID %s удалено", "sogo_profile_reset": "Профиль пользователя SOGo %s сброшен", + "template_added": "Шаблон %s добавлен", + "template_modified": "Изменения шаблона %s сохранены", + "template_removed": "Шаблон ID %s удален", "tls_policy_map_entry_deleted": "Политика TLS ID %s удалено", "tls_policy_map_entry_saved": "Политика TLS \"%s\" сохранена", "ui_texts": "Изменения текстов UI сохранены", @@ -1023,13 +1118,11 @@ "verified_fido2_login": "Авторизация FIDO2 пройдена", "verified_totp_login": "Авторизация TOTP пройдена", "verified_webauthn_login": "Авторизация WebAuthn пройдена", - "verified_yotp_login": "Авторизация Yubico OTP пройдена", - "cors_headers_edited": "Настройки CORS сохранены", - "domain_footer_modified": "Изменения в нижнем колонтитуле домена %s сохранены", - "f2b_banlist_refreshed": "Идентификатор банлиста был успешно обновлен." + "verified_yotp_login": "Авторизация Yubico OTP пройдена" }, "tfa": { "api_register": "%s использует Yubico Cloud API. Пожалуйста, получите ключ API для вашего ключа здесь", + "authenticators": "Аутентификаторы", "confirm": "Подтвердите", "confirm_totp_token": "Пожалуйста, подтвердите изменения, введя сгенерированный код", "delete_tfa": "Отключить TFA", @@ -1048,11 +1141,12 @@ "tfa": "Двухфакторная проверка подлинности", "tfa_token_invalid": "Неправильный TFA токен", "totp": "OTP (Authy, Google Authenticator и др.)", - "webauthn": "WebAuthn аутентификация", + "u2f_deprecated": "Похоже, что ваш ключ был зарегистрирован с использованием устаревшего метода U2F. Мы деактивируем для вас двухфакторную аутентификацию и удалим ваш ключ.", + "u2f_deprecated_important": "Пожалуйста, зарегистрируйте ваш ключ в панели администратора с помощью нового метода WebAuthn.", "waiting_usb_auth": "Ожидание устройства USB...

Пожалуйста, нажмите кнопку на USB устройстве сейчас.", "waiting_usb_register": "Ожидание устройства USB...

Пожалуйста, введите пароль выше и подтвердите регистрацию, нажав кнопку на USB устройстве.", - "yubi_otp": "Yubico OTP аутентификация", - "u2f_deprecated": "Похоже, что ваш ключ был зарегистрирован с использованием устаревшего метода U2F. Мы деактивируем для вас двухфакторную аутентификацию и удалим ваш ключ." + "webauthn": "WebAuthn аутентификация", + "yubi_otp": "Yubico OTP аутентификация" }, "user": { "action": "Действия", @@ -1069,13 +1163,17 @@ "alias_valid_until": "Действителен до", "aliases_also_send_as": "Разрешено отправлять письма от имени", "aliases_send_as_all": "Разрешено отправлять письма от любого имени для домена и его псевдонимов", + "allowed_protocols": "Разрешенные протоколы", "app_hint": "Пароли приложений - это альтернативные пароли для авторизации в IMAP, SMTP, CalDAV, CardDAV и EAS. При этом имя пользователя остается неизменным.
SOGo недоступен через пароли приложений.", "app_name": "Название приложения", "app_passwds": "Пароли приложений", "apple_connection_profile": "Профиль подключения Apple", "apple_connection_profile_complete": "Этот профиль включает настройки IMAP и SMTP, а также CalDAV (календарей) и CardDAV (контактов) для устройства Apple.", "apple_connection_profile_mailonly": "Этот профиль включает только настройки IMAP и SMTP для устройства Apple.", + "apple_connection_profile_with_app_password": "Новый пароль приложения генерируется и добавляется в профиль, поэтому при настройке устройства не требуется вводить пароль. Не предоставляйте доступ к файлу, поскольку он предоставляет полный доступ к вашему почтовому ящику.", + "attribute": "Атрибут", "change_password": "Изменить пароль", + "change_password_hint_app_passwords": "В вашей учетной записи есть {{number_of_app_passwords}} паролей приложений, которые не будут изменены. Чтобы управлять ими, перейдите на вкладку \"Пароли приложений\".", "clear_recent_successful_connections": "Очистить историю успешных подключений", "client_configuration": "Показать руководство по настройке почтовых клиентов и смартфонов", "create_app_passwd": "Создать новый пароль", @@ -1086,6 +1184,7 @@ "delete_ays": "Пожалуйста, подтвердите удаление", "direct_aliases": "Личные псевдонимы", "direct_aliases_desc": "На личные псевдонимы распространяются фильтры нежелательной почты и параметры политики TLS.", + "direct_protocol_access": "Этот пользователь почтового ящика имеет прямой, внешний доступ к следующим протоколам и приложениям. Эта настройка контролируется вашим администратором. Для предоставления доступа к отдельным протоколам и приложениям могут быть созданы пароли приложений.
Кнопка \"Вход в веб-почту\" обеспечивает единый вход в SOGo и всегда доступна.", "eas_reset": "Сбросить кеш ActiveSync устройств", "eas_reset_help": "Во многих случаях сброс кеша устройств помогает восстановить повреждённый профиль ActiveSync.
Внимание: все письма, календари и контакты будут загружены заново на все ваши устройства!", "eas_reset_now": "Сбросить кеш сейчас", @@ -1130,15 +1229,18 @@ "password": "Пароль", "password_now": "Текущий пароль (подтверждение изменения)", "password_repeat": "Подтверждение пароля (повтор)", + "password_reset_info": "If no email for password recovery is provided, this function cannot be used.", "pushover_evaluate_x_prio": "Установить высокий приоритет уведомлений для писем с высоким приоритетом [X-Priority: 1]", "pushover_info": "Настройки Push-уведомления будут применяться ко всей почте %s (за исключением спама), включая псевдонимы (личные, общие и тегированные).", "pushover_only_x_prio": "Получать уведомления только о письмах с высоким приоритетом [X-Priority: 1]", "pushover_sender_array": "Получать уведомления от списка адресов электронной почты (envelop-from, разделённые запятыми):", "pushover_sender_regex": "Получать уведомления от отправителей, удовлетворяющих regex-выражению:", + "pushover_sound": "Звук уведомления", "pushover_text": "Текст уведомления", "pushover_title": "Заголовок уведомления", "pushover_vars": "Когда фильтрация по отправителю не определена, уведомения будут доставлятся от всех отправителей.
Можно использовать обычный фильтр по отправителю и расширенный regex-фильтр, а также оба сразу.
Пожалуйста, ознакомьтесь с Pushover Privacy Policy перед использованием шаблонов для текста и заголовка", "pushover_verify": "Проверить доступ", + "pw_recovery_email": "Адрес для восстановления пароля", "q_add_header": "Нежелательная почта", "q_all": "Все категории", "q_reject": "Отклонённая почта", @@ -1179,15 +1281,15 @@ "spamfilter_yellow": "Жёлтый: эти письма могут быть спамом, будут доставлены в папку \"Спам\"", "status": "Статус", "sync_jobs": "Задания синхронизации", + "syncjob_EXIT_AUTHENTICATION_FAILURE": "Ошибка авторизации", + "syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Неправильное имя пользователя или пароль", + "syncjob_EXIT_CONNECTION_FAILURE": "Ошибка связи с сервером", + "syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Не удалось подключиться к удаленному серверу", + "syncjob_EXIT_OVERQUOTA": "Целевой почтовый ящик превысил квоту", + "syncjob_EXIT_TLS_FAILURE": "Ошибка установки шифрованного соединения", + "syncjob_EX_OK": "Успешно", "syncjob_check_log": "Проверить журнал", "syncjob_last_run_result": "Результат", - "syncjob_EX_OK": "Успешно", - "syncjob_EXIT_CONNECTION_FAILURE": "Ошибка связи с сервером", - "syncjob_EXIT_TLS_FAILURE": "Ошибка установки шифрованного соединения", - "syncjob_EXIT_AUTHENTICATION_FAILURE": "Ошибка авторизации", - "syncjob_EXIT_OVERQUOTA": "Целевой почтовый ящик превысил квоту", - "syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Не удалось подключиться к удаленному серверу", - "syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Неправильное имя пользователя или пароль", "tag_handling": "Обработка тегированной почты", "tag_help_example": "Пример тегированного адреса электронной почты: ich+Facebook@example.org", "tag_help_explain": "Переместить в подпапку: будет создана новая подпапка в INBOX с именем тега, например: \"INBOX/Facebook\".
\r\n Добавить к теме письма: имя тега будет добавлено к теме письма, например: \"[Facebook] My News\".", @@ -1202,20 +1304,15 @@ "tls_policy_warning": "Предупреждение: Если вы включите принудительное шифрованние почты, вы можете столкнуться с потерей писем.
Сообщения, которые не соответствуют политике, будут отбрасываться с сообщением почтовым сервером о серьёзном сбое.
Этот параметр применяется к вашему основному адресу электронной почты (логину), всем личным псевдонимам и псевдонимам доменов. Подразумеваются только псевдонимы с одним почтовым ящиком, как получатель.", "user_settings": "Настройки пользователя", "username": "Имя пользователя", + "value": "Значение", "verify": "Проверить", "waiting": "В ожидании", "week": "неделю", "weekly": "Раз в неделю", "weeks": "недели", - "year": "год", - "years": "лет", - "allowed_protocols": "Разрешенные протоколы", - "apple_connection_profile_with_app_password": "Новый пароль приложения генерируется и добавляется в профиль, поэтому при настройке устройства не требуется вводить пароль. Не предоставляйте доступ к файлу, поскольку он предоставляет полный доступ к вашему почтовому ящику.", - "direct_protocol_access": "Этот пользователь почтового ящика имеет прямой, внешний доступ к следующим протоколам и приложениям. Эта настройка контролируется вашим администратором. Для предоставления доступа к отдельным протоколам и приложениям могут быть созданы пароли приложений.
Кнопка \"Вход в веб-почту\" обеспечивает единый вход в SOGo и всегда доступна.", "with_app_password": "с паролем приложения", - "change_password_hint_app_passwords": "В вашей учетной записи есть {{number_of_app_passwords}} паролей приложений, которые не будут изменены. Чтобы управлять ими, перейдите на вкладку \"Пароли приложений\".", - "attribute": "Атрибут", - "value": "Значение" + "year": "год", + "years": "лет" }, "warning": { "cannot_delete_self": "Вы не можете удалить сами себя", @@ -1229,10 +1326,5 @@ "quota_exceeded_scope": "Квота домена превышена: могут быть созданы только почтовые ящики без лимита.", "session_token": "Неверный токен формы: несоответствие токена", "session_ua": "Неверный токен формы: ошибка проверки User-Agent" - }, - "datatables": { - "collapse_all": "Свернуть все", - "expand_all": "Развернуть все", - "infoPostFix": "" } } From 59c68f2603680ca961d14021842e750cd2ec5ae0 Mon Sep 17 00:00:00 2001 From: milkmaker Date: Sun, 1 Dec 2024 17:49:10 +0100 Subject: [PATCH 21/23] Translations update from Weblate (#6190) --- data/web/lang/lang.fr-fr.json | 2 +- data/web/lang/lang.ru-ru.json | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/data/web/lang/lang.fr-fr.json b/data/web/lang/lang.fr-fr.json index d87632c68..386a4dfc6 100644 --- a/data/web/lang/lang.fr-fr.json +++ b/data/web/lang/lang.fr-fr.json @@ -207,7 +207,7 @@ "include_exclude_info": "Par défaut - sans sélection - toutes les boîte de réception sont adressées", "includes": "Inclure ces destinataires", "last_applied": "Dernière application", - "license_info": "Une licence n’est pas requise, mais contribue au développement.
Enregistrer votre GUID ici or acheter le support pour votre intallation Mailcow.", + "license_info": "Une licence n’est pas requise, mais contribue au développement.
Enregistrer votre GUID ici ou acheter le support pour votre installation Mailcow.", "link": "Lien", "loading": "Veuillez patienter…", "logo_info": "Votre image sera redimensionnée à une hauteur de 40 pixels pour la barre de navigation du haut et à un maximum de 250 pixels en largeur pour la page d'accueil. Un graphique extensible est fortement recommandé.", diff --git a/data/web/lang/lang.ru-ru.json b/data/web/lang/lang.ru-ru.json index ec86d7d06..5d1ef9af2 100644 --- a/data/web/lang/lang.ru-ru.json +++ b/data/web/lang/lang.ru-ru.json @@ -217,7 +217,7 @@ "include_exclude_info": "По умолчанию - без выбора - все почтовые ящики адресованы", "includes": "Включить этих получателей", "ip_check": "Проверить IP", - "ip_check_disabled": "Проверка IP отключена. Вы можете включить его в разделе
Система > Конфигурация > Параметры > Персонализация.", + "ip_check_disabled": "Проверка IP-адресов отключена. Вы можете включить её в разделе
Система > Конфигурация > Параметры > Персонализация.", "ip_check_opt_in": "Согласие на использование сторонних служб ipv4.mailcow.email и ipv6.mailcow.email для разрешения внешних IP-адресов.", "is_mx_based": "На основе MX", "last_applied": "Посл. применение", @@ -228,7 +228,7 @@ "logo_dark_label": "Инвертированный для темного режима", "logo_info": "Ваше изображение будет масштабироваться до высоты 40px для верхней панели навигации и до 250px ширины для стартовой страницы.
Рекомендуется использовать векторную графику, например: .svg.", "logo_normal_label": "Обычный", - "lookup_mx": "Назначение на основе резовинга MX записи по регулярному выражению (.*\\.example\\.com$ для маршрутизации всей почты через этот хост, если MX заканчивающийся на example.com)", + "lookup_mx": "Назначение - регулярное выражение для сопоставления с именем MX (.*\\.google\\.com для направления всей почты, адресованной MX, заканчивающейся на google.com, через этот хоп)", "main_name": "Название для \"mailcow UI\"", "merged_vars_hint": "Серым цветом выделены строки полученные из vars.(local.)inc.php, они не могут быть изменены.", "message": "Сообщение", @@ -512,7 +512,17 @@ "processing": "Пожалуйста, подождите...", "search": "Поиск:", "thousands": " ", - "zeroRecords": "Не найдено соответствующих записей" + "zeroRecords": "Не найдено соответствующих записей", + "paginate": { + "first": "Первая", + "last": "Последняя", + "next": "Следующая", + "previous": "Предыдущая" + }, + "aria": { + "sortAscending": ": активируйте для сортировки столбца по возрастанию", + "sortDescending": ": активируйте для сортировки столбца по убыванию" + } }, "debug": { "architecture": "Архитектура", @@ -736,7 +746,7 @@ "header": { "administration": "Настройка сервера", "apps": "Приложения", - "debug": "Состояние сервера", + "debug": "Информация", "email": "E-Mail", "mailcow_config": "Конфигурация", "mailcow_system": "Система", @@ -900,7 +910,7 @@ "sieve_preset_5": "Auto responder (vacation)", "sieve_preset_6": "Reject mail with response", "sieve_preset_7": "Redirect and keep/drop", - "sieve_preset_8": "Discard message sent to an alias address the sender is part of", + "sieve_preset_8": "Переслать письмо от определенного отправителя, пометить его как прочитанное и поместить в подпапку", "sieve_preset_header": "Пожалуйста, ознакомьтесь с примерами ниже. Для более подробной информации прочитайте Sieve Wikipedia.", "sogo_visible": "Отображать псевдоним в SOGo", "sogo_visible_n": "Не отображать псевдоним в SOGo", @@ -1016,7 +1026,8 @@ "queue_manager": "Очередь на отправку", "show_message": "Показать сообщение", "unhold_mail": "Высвободить", - "unhold_mail_legend": "Освобождает выбранные письма для доставки. (Требуется предварительное удержание)" + "unhold_mail_legend": "Освобождает выбранные письма для доставки. (Требуется предварительное удержание)", + "unban": "освободить очередь" }, "ratelimit": { "day": "сообщений / день", @@ -1173,7 +1184,7 @@ "apple_connection_profile_with_app_password": "Новый пароль приложения генерируется и добавляется в профиль, поэтому при настройке устройства не требуется вводить пароль. Не предоставляйте доступ к файлу, поскольку он предоставляет полный доступ к вашему почтовому ящику.", "attribute": "Атрибут", "change_password": "Изменить пароль", - "change_password_hint_app_passwords": "В вашей учетной записи есть {{number_of_app_passwords}} паролей приложений, которые не будут изменены. Чтобы управлять ими, перейдите на вкладку \"Пароли приложений\".", + "change_password_hint_app_passwords": "В вашей учетной записи есть %d паролей приложений, которые не будут изменены. Чтобы управлять ими, перейдите на вкладку \"Пароли приложений\".", "clear_recent_successful_connections": "Очистить историю успешных подключений", "client_configuration": "Показать руководство по настройке почтовых клиентов и смартфонов", "create_app_passwd": "Создать новый пароль", From f3060b37a6f58e52500b87c2a59026af93e8c99d Mon Sep 17 00:00:00 2001 From: milkmaker Date: Sun, 1 Dec 2024 17:49:28 +0100 Subject: [PATCH 22/23] update postscreen_access.cidr (#6189) --- data/conf/postfix/postscreen_access.cidr | 76 ++++++------------------ 1 file changed, 17 insertions(+), 59 deletions(-) diff --git a/data/conf/postfix/postscreen_access.cidr b/data/conf/postfix/postscreen_access.cidr index 7a1cbf6ae..1dbe94773 100644 --- a/data/conf/postfix/postscreen_access.cidr +++ b/data/conf/postfix/postscreen_access.cidr @@ -1,6 +1,6 @@ -# Whitelist generated by Postwhite v3.4 on Fri Nov 1 00:18:49 UTC 2024 +# Whitelist generated by Postwhite v3.4 on Sun Dec 1 00:21:36 UTC 2024 # https://github.com/stevejenkins/postwhite/ -# 2013 total rules +# 1971 total rules 2a00:1450:4000::/36 permit 2a01:111:f400::/48 permit 2a01:111:f403:8000::/50 permit @@ -19,8 +19,7 @@ 8.20.114.31 permit 8.25.194.0/23 permit 8.25.196.0/23 permit -8.39.54.0/23 permit -8.40.222.0/23 permit +10.162.0.0/16 permit 12.130.86.238 permit 13.110.208.0/21 permit 13.110.209.0/24 permit @@ -31,9 +30,11 @@ 15.200.21.50 permit 15.200.44.248 permit 15.200.201.185 permit +17.41.0.0/16 permit 17.57.155.0/24 permit 17.57.156.0/24 permit 17.58.0.0/16 permit +17.142.0.0/15 permit 17.143.234.140/30 permit 18.156.89.250 permit 18.157.243.190 permit @@ -116,7 +117,6 @@ 40.233.64.216 permit 40.233.83.78 permit 40.233.88.28 permit -43.228.184.0/22 permit 44.206.138.57 permit 44.217.45.156 permit 44.236.56.93 permit @@ -325,7 +325,6 @@ 65.110.161.77 permit 65.123.29.213 permit 65.123.29.220 permit -65.154.166.0/24 permit 65.212.180.36 permit 66.102.0.0/20 permit 66.119.150.192/26 permit @@ -1114,10 +1113,8 @@ 98.139.245.212/31 permit 99.78.197.208/28 permit 99.83.190.102 permit -103.2.140.0/22 permit 103.9.96.0/22 permit 103.28.42.0/24 permit -103.47.204.0/22 permit 103.151.192.0/23 permit 103.168.172.128/27 permit 104.43.243.237 permit @@ -1285,9 +1282,6 @@ 117.120.16.0/21 permit 119.42.242.52/31 permit 119.42.242.156 permit -121.244.91.48 permit -121.244.91.52 permit -122.15.156.182 permit 123.126.78.64/29 permit 124.108.96.24/31 permit 124.108.96.28/31 permit @@ -1348,19 +1342,7 @@ 134.170.141.64/26 permit 134.170.143.0/24 permit 134.170.174.0/24 permit -135.84.80.0/24 permit -135.84.81.0/24 permit -135.84.82.0/24 permit -135.84.83.0/24 permit 135.84.216.0/22 permit -136.143.160.0/24 permit -136.143.161.0/24 permit -136.143.162.0/24 permit -136.143.178.49 permit -136.143.182.0/23 permit -136.143.184.0/24 permit -136.143.188.0/24 permit -136.143.190.0/23 permit 136.147.128.0/20 permit 136.147.135.0/24 permit 136.147.176.0/20 permit @@ -1375,7 +1357,6 @@ 139.138.46.219 permit 139.138.57.55 permit 139.138.58.119 permit -139.167.79.86 permit 139.180.17.0/24 permit 140.238.148.191 permit 141.148.159.229 permit @@ -1410,6 +1391,7 @@ 146.20.215.0/24 permit 146.20.215.182 permit 146.88.28.0/24 permit +147.154.32.0/25 permit 147.243.1.47 permit 147.243.1.48 permit 147.243.1.153 permit @@ -1450,7 +1432,6 @@ 157.151.208.65 permit 157.255.1.64/29 permit 158.101.211.207 permit -158.120.80.0/21 permit 158.247.16.0/20 permit 159.92.154.0/24 permit 159.92.155.0/24 permit @@ -1478,6 +1459,11 @@ 161.38.204.0/22 permit 161.71.32.0/19 permit 161.71.64.0/20 permit +162.88.4.0/23 permit +162.88.8.0/24 permit +162.88.24.0/24 permit +162.88.25.0/24 permit +162.88.36.0/24 permit 162.247.216.0/22 permit 163.47.180.0/22 permit 163.114.130.16 permit @@ -1486,7 +1472,6 @@ 163.114.135.16 permit 164.152.23.32 permit 164.177.132.168/30 permit -165.173.128.0/24 permit 166.78.68.0/22 permit 166.78.68.221 permit 166.78.69.169 permit @@ -1515,12 +1500,6 @@ 168.245.12.252 permit 168.245.46.9 permit 168.245.127.231 permit -169.148.129.0/24 permit -169.148.131.0/24 permit -169.148.142.10 permit -169.148.144.0/25 permit -169.148.144.10 permit -170.10.68.0/22 permit 170.10.128.0/24 permit 170.10.129.0/24 permit 170.10.132.56/29 permit @@ -1626,6 +1605,7 @@ 192.18.139.154 permit 192.18.145.36 permit 192.18.152.58 permit +192.29.103.128/25 permit 192.30.252.0/22 permit 192.161.144.0/20 permit 192.162.87.0/24 permit @@ -1651,14 +1631,6 @@ 195.234.109.226 permit 195.245.230.0/23 permit 198.2.128.0/18 permit -198.2.128.0/24 permit -198.2.132.0/22 permit -198.2.136.0/23 permit -198.2.145.0/24 permit -198.2.177.0/24 permit -198.2.178.0/23 permit -198.2.180.0/24 permit -198.2.186.0/23 permit 198.21.0.0/21 permit 198.37.144.0/20 permit 198.37.152.186 permit @@ -1678,15 +1650,7 @@ 199.16.156.0/22 permit 199.33.145.1 permit 199.33.145.32 permit -199.34.22.36 permit 199.59.148.0/22 permit -199.67.80.2 permit -199.67.80.20 permit -199.67.82.2 permit -199.67.82.20 permit -199.67.84.0/24 permit -199.67.86.0/24 permit -199.67.88.0/24 permit 199.101.161.130 permit 199.101.162.0/25 permit 199.122.120.0/21 permit @@ -1698,7 +1662,6 @@ 202.165.102.47 permit 202.177.148.100 permit 202.177.148.110 permit -203.31.36.0/22 permit 203.32.4.25 permit 203.55.21.0/24 permit 203.81.17.0/24 permit @@ -1744,19 +1707,13 @@ 204.92.114.187 permit 204.92.114.203 permit 204.92.114.204/31 permit -204.141.32.0/23 permit -204.141.42.0/23 permit 204.220.160.0/21 permit 204.220.168.0/21 permit 204.220.176.0/20 permit 204.232.168.0/24 permit 205.139.110.0/24 permit 205.201.128.0/20 permit -205.201.131.128/25 permit -205.201.134.128/25 permit -205.201.136.0/23 permit 205.201.137.229 permit -205.201.139.0/24 permit 205.207.104.0/22 permit 205.220.167.17 permit 205.220.167.98 permit @@ -1784,7 +1741,6 @@ 207.46.132.128/27 permit 207.46.198.0/25 permit 207.46.200.0/27 permit -207.58.147.64/28 permit 207.67.38.0/24 permit 207.67.98.192/27 permit 207.68.176.0/26 permit @@ -1831,6 +1787,8 @@ 208.74.204.5 permit 208.74.204.9 permit 208.75.120.0/22 permit +208.76.62.0/24 permit +208.76.63.0/24 permit 208.82.237.96/29 permit 208.82.237.104/31 permit 208.82.238.96/29 permit @@ -1930,7 +1888,6 @@ 213.199.177.0/26 permit 216.17.150.242 permit 216.17.150.251 permit -216.22.15.224/27 permit 216.24.224.0/20 permit 216.39.60.154/31 permit 216.39.60.156/30 permit @@ -1973,7 +1930,10 @@ 216.136.162.65 permit 216.136.162.120/29 permit 216.136.168.80/28 permit +216.139.64.0/19 permit 216.145.221.0/24 permit +216.146.32.0/24 permit +216.146.33.0/24 permit 216.198.0.0/18 permit 216.203.30.55 permit 216.203.33.178/31 permit @@ -1999,8 +1959,6 @@ 2603:1030:20e:3::23c permit 2603:1030:b:3::152 permit 2603:1030:c02:8::14 permit -2607:13c0:0001:0000:0000:0000:0000:7000/116 permit -2607:13c0:0002:0000:0000:0000:0000:1000/116 permit 2607:f8b0:4000::/36 permit 2620:109:c003:104::/64 permit 2620:109:c003:104::215 permit From 6e8e13cebc45c4e6172fa78c8fb8481d53e0d7e5 Mon Sep 17 00:00:00 2001 From: i-curve Date: Wed, 4 Dec 2024 19:28:14 +0800 Subject: [PATCH 23/23] fix: check docker version fail in generate_config.sh #6187 (#6188) close #6187 Signed-off-by: i-curve Co-authored-by: Niklas Meyer --- generate_config.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate_config.sh b/generate_config.sh index 46a36a179..915a5d907 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -26,7 +26,7 @@ for bin in openssl curl docker git awk sha1sum grep cut; do done # Check Docker Version (need at least 24.X) -docker_version=$(docker -v | grep -oP '\d+\.\d+\.\d+' | cut -d '.' -f 1) +docker_version=$(docker -v | grep -oP '\d+\.\d+\.\d+' | head -n 1 | cut -d '.' -f 1) if [[ $docker_version -lt 24 ]]; then echo -e "\e[31mCannot find Docker with a Version higher or equals 24.0.0\e[0m"