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 01/52] 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 5ad4ab5b60b5f4496d01b623dca44d862cfff868 Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Fri, 15 Nov 2024 16:39:06 +0100 Subject: [PATCH 02/52] 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 03/52] [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 04/52] 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 05/52] [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 06/52] 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 07/52] [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 08/52] [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 09/52] [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 10/52] [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 11/52] [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 12/52] [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 13/52] 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 31185e3de1dd6eeda801c7434eef699918ed205e Mon Sep 17 00:00:00 2001 From: Hassan A Hashim Date: Wed, 27 Nov 2024 14:47:57 +0300 Subject: [PATCH 14/52] Implement search mailboxes by fullname --- data/web/json_api.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/web/json_api.php b/data/web/json_api.php index 86ab21c60..abaf749f1 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -1067,9 +1067,10 @@ if (isset($_GET['query'])) { ['db' => 'last_mail_login', 'dt' => 4, 'dummy' => true, 'order_subquery' => "SELECT MAX(`datetime`) FROM `sasl_log` WHERE `service` != 'SSO' AND `username` = `m`.`username`"], ['db' => 'last_pw_change', 'dt' => 5, 'dummy' => true, 'order_subquery' => "JSON_EXTRACT(attributes, '$.passwd_update')"], ['db' => 'in_use', 'dt' => 6, 'dummy' => true, 'order_subquery' => "(SELECT SUM(bytes) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`) / `m`.`quota`"], + ['db' => 'name', 'dt' => 7], ['db' => 'messages', 'dt' => 17, 'dummy' => true, 'order_subquery' => "SELECT SUM(messages) FROM `quota2` WHERE `quota2`.`username` = `m`.`username`"], ['db' => 'tags', 'dt' => 20, 'dummy' => true, 'search' => ['join' => 'LEFT JOIN `tags_mailbox` AS `tm` ON `tm`.`username` = `m`.`username`', 'where_column' => '`tm`.`tag_name`']], - ['db' => 'active', 'dt' => 21] + ['db' => 'active', 'dt' => 21], ]; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/ssp.class.php'; From 59c68f2603680ca961d14021842e750cd2ec5ae0 Mon Sep 17 00:00:00 2001 From: milkmaker Date: Sun, 1 Dec 2024 17:49:10 +0100 Subject: [PATCH 15/52] 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 16/52] 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 17/52] 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" From d1af52b4e7fdc6e4d6e81526488c13573b7e3165 Mon Sep 17 00:00:00 2001 From: Tatsuya Yokota Date: Fri, 6 Dec 2024 17:44:16 +0900 Subject: [PATCH 18/52] Add initial Japanese language files (#6198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add initial Japanese language files * Reordered language list: moved Japanese (日本語) below Italian (Italiano) --------- Co-authored-by: Tatsuya Yokota --- data/web/inc/vars.inc.php | 1 + data/web/lang/lang.ja-jp.json | 1341 +++++++++++++++++++++++++++++++++ 2 files changed, 1342 insertions(+) create mode 100644 data/web/lang/lang.ja-jp.json diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index d3165b8af..29791cb68 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -93,6 +93,7 @@ $AVAILABLE_LANGUAGES = array( 'gr-gr' => 'Ελληνικά (Greek)', 'hu-hu' => 'Magyar (Hungarian)', 'it-it' => 'Italiano (Italian)', + 'ja-jp' => '日本語 (Japanese)', 'ko-kr' => '한국어 (Korean)', 'lv-lv' => 'latviešu (Latvian)', 'lt-lt' => 'Lietuvių (Lithuanian)', diff --git a/data/web/lang/lang.ja-jp.json b/data/web/lang/lang.ja-jp.json new file mode 100644 index 000000000..963d72358 --- /dev/null +++ b/data/web/lang/lang.ja-jp.json @@ -0,0 +1,1341 @@ +{ + "acl": { + "alias_domains": "エイリアスドメインを追加", + "app_passwds": "アプリパスワードの管理", + "bcc_maps": "BCCマップ", + "delimiter_action": "デリミタの動作", + "domain_desc": "ドメイン説明の変更", + "domain_relayhost": "ドメインのリレーホストを変更", + "eas_reset": "EASデバイスをリセット", + "extend_sender_acl": "外部アドレスで送信者ACLを拡張可能にする", + "filters": "フィルター", + "login_as": "メールボックスユーザーとしてログイン", + "mailbox_relayhost": "メールボックスのリレーホストを変更", + "prohibited": "ACLにより禁止されています", + "protocol_access": "プロトコルアクセスを変更", + "pushover": "Pushover", + "pw_reset": "mailcowユーザーパスワードのリセットを許可", + "quarantine": "隔離操作", + "quarantine_attachments": "隔離添付ファイル", + "quarantine_category": "隔離通知カテゴリの変更", + "quarantine_notification": "隔離通知の変更", + "ratelimit": "レート制限", + "recipient_maps": "受信者マップ", + "smtp_ip_access": "SMTPで許可されるホストの変更", + "sogo_access": "SOGoアクセス管理を許可", + "sogo_profile_reset": "SOGoプロファイルをリセット", + "spam_alias": "一時的なエイリアス", + "spam_policy": "ブラックリスト/ホワイトリスト", + "spam_score": "スパムスコア", + "syncjobs": "同期ジョブ", + "tls_policy": "TLSポリシー", + "unlimited_quota": "メールボックスの無制限クォータ" + }, + "add": { + "activate_filter_warn": "有効を選択すると、他のすべてのフィルターは無効になります。", + "active": "有効", + "add": "追加", + "add_domain_only": "ドメインのみを追加", + "add_domain_restart": "ドメインを追加してSOGoを再起動", + "alias_address": "エイリアスアドレス", + "alias_address_info": "完全なメールアドレスまたは@example.com(ドメインのすべてのメッセージをキャッチ、カンマ区切り)。mailcowドメインのみ対応", + "alias_domain": "エイリアスドメイン", + "alias_domain_info": "有効なドメイン名のみ(カンマ区切り)。", + "app_name": "アプリ名", + "app_password": "アプリパスワードを追加", + "app_passwd_protocols": "アプリパスワードの許可プロトコル", + "automap": "フォルダーを自動マッピングする試み(例:「送信済みアイテム」、「送信済み」=>「送信済み」)", + "backup_mx_options": "リレーオプション", + "bcc_dest_format": "BCC送信先は単一の有効なメールアドレスである必要があります。
複数のアドレスにコピーを送信する必要がある場合は、エイリアスを作成し、ここで使用してください。", + "comment_info": "プライベートコメントはユーザーには表示されませんが、パブリックコメントはユーザーの概要でホバーするとツールチップとして表示されます。", + "custom_params": "カスタムパラメータ", + "custom_params_hint": "正: --param=xy, 誤: --param xy", + "delete1": "完了時にソースから削除", + "delete2": "ソースに存在しないメッセージを送信先で削除", + "delete2duplicates": "送信先で重複を削除", + "description": "説明", + "destination": "送信先", + "disable_login": "ログインを禁止(受信メールは許可)", + "domain": "ドメイン", + "domain_matches_hostname": "ドメイン %s はホスト名と一致します", + "domain_quota_m": "ドメインの総クォータ(MiB)", + "dry": "同期をシミュレーション", + "enc_method": "暗号化方式", + "exclude": "オブジェクトを除外(正規表現)", + "full_name": "フルネーム", + "gal": "グローバルアドレスリスト", + "gal_info": "GALにはドメインのすべてのオブジェクトが含まれ、どのユーザーも編集できません。無効にすると、SOGoの空き/取り込み情報が表示されなくなります!変更を適用するにはSOGoを再起動してください。", + "generate": "生成", + "goto_ham": "ハムとして学習", + "goto_null": "メールを静かに破棄", + "goto_spam": "スパムとして学習", + "hostname": "ホスト名", + "inactive": "無効", + "kind": "種類", + "mailbox_quota_def": "デフォルトのメールボックスクォータ", + "mailbox_quota_m": "メールボックスごとの最大クォータ(MiB)", + "mailbox_username": "ユーザー名(メールアドレスの左部分)", + "max_aliases": "作成可能なエイリアスの最大数", + "max_mailboxes": "作成可能なメールボックスの最大数", + "mins_interval": "ポーリング間隔(分)", + "multiple_bookings": "複数の予約", + "nexthop": "次のホップ", + "password": "パスワード", + "password_repeat": "確認用パスワード(再入力)", + "port": "ポート", + "post_domain_add": "新しいドメインを追加した後、SOGoコンテナ \"sogo-mailcow\" を再起動する必要があります。

さらに、ドメインのDNS設定を確認してください。DNS設定が承認されたら、\"acme-mailcow\" を再起動して新しいドメインの証明書を自動生成してください(autoconfig.<domain>, autodiscover.<domain>)。
この手順は任意で、24時間ごとに再試行されます。", + "private_comment": "プライベートコメント", + "public_comment": "パブリックコメント", + "quota_mb": "クォータ(MiB)", + "relay_all": "すべての受信者をリレー", + "relay_all_info": "すべての受信者をリレーしない場合、リレーする各受信者のために(\"ブラインド\")メールボックスを追加する必要があります。", + "relay_domain": "このドメインをリレー", + "relay_transport_info": "
情報
このドメインのカスタム送信先のトランスポートマップを定義できます。設定されていない場合は、MXルックアップが実行されます。", + "relay_unknown_only": "存在しないメールボックスのみをリレー。存在するメールボックスはローカルに配信されます。", + "relayhost_wrapped_tls_info": "TLSラップされたポート(主にポート465で使用)を使用しないでください
\r\n非ラップポートを使用し、STARTTLSを発行してください。TLSを強制するTLSポリシーを \"TLSポリシーマップ\" に作成できます。", + "select": "選択してください...", + "select_domain": "最初にドメインを選択してください", + "sieve_desc": "簡単な説明", + "sieve_type": "フィルタータイプ", + "skipcrossduplicates": "フォルダー間で重複するメッセージをスキップ(先着順)", + "subscribeall": "すべてのフォルダーを購読", + "syncjob": "同期ジョブを追加", + "syncjob_hint": "パスワードはプレーンテキストで保存される必要があることに注意してください!", + "tags": "タグ", + "target_address": "宛先アドレス", + "target_address_info": "完全なメールアドレス(カンマ区切り)。", + "target_domain": "ターゲットドメイン", + "timeout1": "リモートホストへの接続タイムアウト", + "timeout2": "ローカルホストへの接続タイムアウト", + "username": "ユーザー名", + "validate": "検証", + "validation_success": "正常に検証されました" + }, + "admin": { + "access": "アクセス", + "action": "アクション", + "activate_api": "APIを有効化", + "activate_send": "送信ボタンを有効化", + "active": "アクティブ", + "active_rspamd_settings_map": "アクティブな設定マップ", + "add": "追加", + "add_admin": "管理者を追加", + "add_domain_admin": "ドメイン管理者を追加", + "add_forwarding_host": "転送ホストを追加", + "add_relayhost": "送信者依存のトランスポートを追加", + "add_relayhost_hint": "認証データがある場合、それはプレーンテキストとして保存されることに注意してください。", + "add_row": "行を追加", + "add_settings_rule": "設定ルールを追加", + "add_transport": "トランスポートを追加", + "add_transports_hint": "認証データがある場合、それはプレーンテキストとして保存されることに注意してください。", + "additional_rows": " 追加の行が追加されました", + "admin": "管理者", + "admin_details": "管理者の詳細を編集", + "admin_domains": "ドメインの割り当て", + "admins": "管理者", + "admins_ldap": "LDAP 管理者", + "advanced_settings": "詳細設定", + "allowed_methods": "Access-Control-Allow-Methods", + "allowed_origins": "Access-Control-Allow-Origin", + "api_allow_from": "これらのIPまたはCIDRネットワーク表記からのAPIアクセスを許可", + "api_info": "APIは進行中の作業です。ドキュメントは/apiにあります。", + "api_key": "APIキー", + "api_read_only": "読み取り専用アクセス", + "api_read_write": "読み取り書き込みアクセス", + "api_skip_ip_check": "APIのIPチェックをスキップ", + "app_links": "アプリリンク", + "app_name": "アプリ名", + "apps_name": "\"mailcow アプリ\" の名前", + "arrival_time": "到着時刻 (サーバー時刻)", + "authed_user": "認証済みユーザー", + "ays": "本当に続行しますか?", + "ban_list_info": "以下に禁止されたIPのリストを表示します: ネットワーク (残りの禁止時間) - [アクション]
キューに入れられたIPは、数秒以内にアクティブな禁止リストから削除されます。
赤いラベルはブラックリストによる永久禁止を示します。", + "change_logo": "ロゴを変更", + "logo_normal_label": "通常", + "logo_dark_label": "ダークモード用の反転", + "configuration": "設定", + "convert_html_to_text": "HTMLをプレーンテキストに変換", + "copy_to_clipboard": "テキストがクリップボードにコピーされました!", + "cors_settings": "CORS設定", + "credentials_transport_warning": "警告: 新しいトランスポートマップエントリを追加すると、一致するネクストホップ列を持つすべてのエントリの認証情報が更新されます。", + "customer_id": "顧客ID", + "customize": "カスタマイズ", + "destination": "宛先", + "dkim_add_key": "ARC/DKIMキーを追加", + "dkim_domains_selector": "セレクタ", + "dkim_domains_wo_keys": "キーが不足しているドメインを選択", + "dkim_from": "From", + "dkim_from_title": "データをコピーする元のドメイン", + "dkim_key_length": "DKIMキー長 (ビット)", + "dkim_key_missing": "キーが不足しています", + "dkim_key_unused": "キーが未使用です", + "dkim_key_valid": "キーが有効です", + "dkim_keys": "ARC/DKIMキー", + "dkim_overwrite_key": "既存のDKIMキーを上書き", + "dkim_private_key": "プライベートキー", + "dkim_to": "To", + "dkim_to_title": "対象のドメイン - 上書きされます", + "domain": "ドメイン", + "domain_admin": "ドメイン管理者", + "domain_admins": "ドメイン管理者", + "domain_s": "ドメイン", + "duplicate": "重複", + "duplicate_dkim": "DKIMレコードを複製", + "edit": "編集", + "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パラメータ", + "f2b_regex_info": "考慮されるログ: SOGo、Postfix、Dovecot、PHP-FPM。", + "f2b_retry_window": "最大試行回数のための再試行ウィンドウ (秒)", + "f2b_whitelist": "ホワイトリスト登録されたネットワーク/ホスト", + "filter_table": "フィルターテーブル", + "forwarding_hosts": "転送ホスト", + "forwarding_hosts_add_hint": "IPv4/IPv6アドレス、CIDR表記のネットワーク、ホスト名(IPアドレスに解決されます)、またはドメイン名(SPFレコードやMXレコードを照会してIPアドレスに解決)を指定できます。", + "forwarding_hosts_hint": "ここにリストされたホストからのメッセージは無条件に受け入れられます。これらのホストは、DNSBL(ブラックリスト)やグレイリスティングのチェック対象外となります。これらのホストから受信したスパムは拒否されませんが、オプションで迷惑メールフォルダーに振り分けることができます。最も一般的な使用例は、受信メールをmailcowサーバーに転送するように設定したメールサーバーを指定することです。", + "from": "送信元", + "generate": "生成", + "guid": "GUID - 一意のインスタンスID", + "guid_and_license": "GUID & ライセンス", + "hash_remove_info": "レート制限ハッシュ(まだ存在する場合)を削除すると、そのカウンターが完全にリセットされます。
\r\n各ハッシュは個別の色で表示されます。", + "help_text": "ログイン画面下部に表示するヘルプテキスト(HTML使用可能)", + "host": "ホスト", + "html": "HTML", + "import": "インポート", + "import_private_key": "プライベートキーをインポート", + "in_use_by": "使用中", + "inactive": "非アクティブ", + "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": "ライセンスは必須ではありませんが、さらなる開発を支援します。
こちらでGUIDを登録するか、mailcowインストールのサポートを購入してください。", + "link": "リンク", + "loading": "お待ちください...", + "login_time": "ログイン時間", + "logo_info": "ナビゲーションバーの高さを40px、スタートページの最大幅を250pxにスケーリングします。スケーラブルな画像を推奨します。", + "lookup_mx": "宛先がMX名に一致する正規表現です(例:.*\\.google\\.com はgoogle.comで終わるMX宛のすべてのメールをこのホップにルートします)", + "main_name": "\"mailcow UI\" 名称", + "merged_vars_hint": "グレー表示された行はvars.(local.)inc.phpからマージされており、変更できません。", + "message": "メッセージ", + "message_size": "メッセージサイズ", + "nexthop": "次のホップ", + "no": "✕", + "no_active_bans": "アクティブな禁止はありません", + "no_new_rows": "新しい行はありません", + "no_record": "レコードなし", + "oauth2_apps": "OAuth2 アプリ", + "oauth2_add_client": "OAuth2クライアントを追加", + "oauth2_client_id": "クライアントID", + "oauth2_client_secret": "クライアントシークレット", + "oauth2_info": "OAuth2は、\"Authorization Code\"のグラントタイプをサポートし、リフレッシュトークンを発行します。
\r\nサーバーはリフレッシュトークンが使用された後、自動的に新しいリフレッシュトークンを発行します。

\r\n• デフォルトのスコープはprofileです。メールボックスユーザーのみOAuth2認証が可能です。スコープパラメータが省略されると、デフォルトでprofileに戻ります。
\r\n• stateパラメータは、クライアントが認可リクエストの一部として送信する必要があります。

\r\nOAuth2 APIリクエスト用のパス:
\r\n
    \r\n
  • 認可エンドポイント: /oauth/authorize
  • \r\n
  • トークンエンドポイント: /oauth/token
  • \r\n
  • リソースページ: /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": "パスワードポリシー", + "password_policy_chars": "少なくとも1文字のアルファベットを含む必要があります", + "password_policy_length": "パスワードの最小長は %d です", + "password_policy_lowerupper": "大文字と小文字を含む必要があります", + "password_policy_numbers": "少なくとも1つの数字を含む必要があります", + "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": "隔離", + "quarantine_bcc": "すべての通知をコピー(BCC)でこの受信者に送信:
無効にする場合は空欄にしてください。署名なし、チェックされていないメール。内部でのみ配信してください。", + "quarantine_exclude_domains": "ドメインおよびエイリアスドメインを除外", + "quarantine_max_age": "最大保存期間(日数)
値は1日以上である必要があります。", + "quarantine_max_score": "スパムスコアがこの値を超えるメールの通知を破棄:
デフォルトは9999.0です。", + "quarantine_max_size": "最大サイズ(MiB)(これを超える要素は破棄されます):
0は無制限ではありません", + "quarantine_notification_html": "通知メールのテンプレート:
空欄にするとデフォルトテンプレートに戻ります。", + "quarantine_notification_sender": "通知メールの送信者", + "quarantine_notification_subject": "通知メールの件名", + "quarantine_redirect": "すべての通知をリダイレクトしてこの受信者に送信:
無効にする場合は空欄にしてください。署名なし、チェックされていないメール。内部でのみ配信してください。", + "quarantine_release_format": "リリースされたアイテムの形式", + "quarantine_release_format_att": "添付ファイルとして", + "quarantine_release_format_raw": "変更されていないオリジナル", + "quarantine_retention_size": "メールボックスごとの保持量:
0は無効を示します。", + "quota_notification_html": "通知メールのテンプレート:
空欄にするとデフォルトテンプレートに戻ります。", + "quota_notification_sender": "通知メールの送信者", + "quota_notification_subject": "通知メールの件名", + "quota_notifications": "クォータ通知", + "quota_notifications_info": "クォータ通知は、使用率が80%と95%を超えたときに1回ずつユーザーに送信されます。", + "quota_notifications_vars": "{{percent}} はユーザーの現在のクォータ使用率を表します
{{username}} はメールボックスの名前です", + "queue_unban": "禁止解除", + "r_active": "アクティブな制限", + "r_inactive": "非アクティブな制限", + "r_info": "アクティブな制限リストでグレーアウトまたは無効化された要素は、mailcowで有効な制限として認識されていませんが、リストの順序に従って設定されます。
新しい要素をinc/vars.local.inc.phpに追加することで切り替え可能にできます。", + "rate_name": "レート名", + "recipients": "受信者", + "refresh": "更新", + "regen_api_key": "APIキーを再生成", + "regex_maps": "正規表現マップ", + "relay_from": "\"送信元:\" アドレス", + "relay_rcpt": "\"受信先:\" アドレス", + "relay_run": "テストを実行", + "relayhosts": "送信者依存のトランスポート", + "relayhosts_hint": "送信者依存のトランスポートを定義し、ドメイン構成ダイアログで選択できるようにします。
\r\n トランスポートサービスは常に \"smtp:\" であり、提供された場合はTLSを試みます。TLSラップ(SMTPS)はサポートされていません。ユーザーごとの個別の送信TLSポリシー設定が考慮されます。
\r\n 選択したドメインおよびエイリアスドメインに影響します。", + "remove": "削除", + "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": "ルールの内容", + "rsetting_desc": "簡単な説明", + "rsetting_no_selection": "ルールを選択してください", + "rsetting_none": "利用可能なルールがありません", + "rsettings_insert_preset": "例のプリセット \"%s\" を挿入", + "rsettings_preset_1": "認証済みユーザーに対してDKIMとレート制限以外を無効化", + "rsettings_preset_2": "ポストマスターはスパムを許可", + "rsettings_preset_3": "特定の送信者のみを許可(内部メールボックスとして使用)", + "rsettings_preset_4": "ドメインに対するRspamdを無効化", + "rspamd_com_settings": "設定名は自動生成されます。以下の例のプリセットをご覧ください。詳細についてはRspamdのドキュメントを参照してください。", + "rspamd_global_filters": "グローバルフィルターマップ", + "rspamd_global_filters_agree": "注意して操作します!", + "rspamd_global_filters_info": "グローバルフィルターマップには、ブラックリストやホワイトリストなど、さまざまな種類が含まれています。", + "rspamd_global_filters_regex": "名前はその目的を説明します。すべてのコンテンツは、\"/パターン/オプション\"(例: /.+@domain\\.tld/i)の形式で有効な正規表現を含む必要があります。
\r\n 正規表現の各行に対して簡易チェックが実行されますが、Rspamdが構文を正しく読み取れない場合、機能が損なわれる可能性があります。
\r\n マップの内容が変更された場合、Rspamdはそれを読み取ろうとします。問題が発生した場合は、Rspamdを再起動してマップの再読み込みを強制してください。
ブラックリストに登録された要素は隔離の対象外です。", + "rspamd_settings_map": "Rspamd設定マップ", + "sal_level": "Mooレベル", + "save": "変更を保存", + "search_domain_da": "ドメインを検索", + "send": "送信", + "sender": "送信者", + "service": "サービス", + "service_id": "サービスID", + "source": "ソース", + "spamfilter": "スパムフィルター", + "subject": "件名", + "success": "成功", + "sys_mails": "システムメール", + "text": "テキスト", + "time": "時間", + "title": "タイトル", + "title_name": "\"mailcow UI\" サイトのタイトル", + "to_top": "トップへ戻る", + "transport_dest_format": "正規表現または構文: example.org, .example.org, *, box@example.org(複数の値はカンマ区切りで指定可能)", + "transport_maps": "トランスポートマップ", + "transport_test_rcpt_info": "• 外部宛先へのリレーをテストするには null@hosted.mailcow.de を使用してください。", + "transports_hint": "• トランスポートマップのエントリーは送信者依存のトランスポートマップを上書きします。
\r\n• MXベースのトランスポートが優先的に使用されます。
\r\n• ユーザーごとの送信TLSポリシー設定は無視され、TLSポリシーマップエントリーによってのみ強制できます。
\r\n• 定義されたトランスポートのトランスポートサービスは常に \"smtp:\" であり、提供された場合はTLSを試みます。TLSラップ(SMTPS)はサポートされていません。
\r\n• アドレスが \"/localhost$/\" に一致する場合、それらは常に \"local:\" 経由で転送されるため、\"*\" 宛先はこれらのアドレスには適用されません。
\r\n• 次のホップ \"[host]:25\" の認証情報を判定するために、Postfixは常に \"host\" を最初に照会し、その後 \"[host]:25\" を検索します。この動作により、\"host\" と \"[host]:25\" を同時に使用することはできません。", + "ui_footer": "フッター(HTML使用可)", + "ui_header_announcement": "お知らせ", + "ui_header_announcement_active": "お知らせを有効に設定", + "ui_header_announcement_content": "テキスト(HTML使用可)", + "ui_header_announcement_help": "お知らせはすべてのログイン済みユーザーとUIのログイン画面に表示されます。", + "ui_header_announcement_select": "お知らせタイプを選択", + "ui_header_announcement_type": "タイプ", + "ui_header_announcement_type_danger": "非常に重要", + "ui_header_announcement_type_info": "情報", + "ui_header_announcement_type_warning": "重要", + "ui_texts": "UIラベルとテキスト", + "unban_pending": "禁止解除保留中", + "unchanged_if_empty": "変更がない場合は空白のままにしてください", + "upload": "アップロード", + "username": "ユーザー名", + "validate_license_now": "GUIDをライセンスサーバーで検証", + "verify": "確認", + "yes": "✓" + }, + "danger": { + "access_denied": "アクセスが拒否されるか、フォームデータが無効です", + "alias_domain_invalid": "エイリアスドメイン %s は無効です", + "alias_empty": "エイリアスアドレスを空にすることはできません", + "alias_goto_identical": "エイリアスと転送先アドレスは同一であってはなりません", + "alias_invalid": "エイリアスアドレス %s は無効です", + "aliasd_targetd_identical": "エイリアスドメインはターゲットドメインと同じであってはなりません: %s", + "aliases_in_use": "最大エイリアス数は %d 以上である必要があります", + "app_name_empty": "アプリ名を空にすることはできません", + "app_passwd_id_invalid": "アプリパスワードID %s は無効です", + "bcc_empty": "BCC送信先を空にすることはできません", + "bcc_exists": "BCCマップ %s がタイプ %s に存在します", + "bcc_must_be_email": "BCC送信先 %s は有効なメールアドレスではありません", + "comment_too_long": "コメントが長すぎます。最大160文字まで許可されます", + "cors_invalid_method": "指定された許可メソッドが無効です", + "cors_invalid_origin": "指定された許可オリジンが無効です", + "defquota_empty": "メールボックスごとのデフォルトクォータを0にすることはできません。", + "demo_mode_enabled": "デモモードが有効です", + "description_invalid": "リソース %s の説明が無効です", + "dkim_domain_or_sel_exists": "\"%s\" のDKIMキーが既に存在しており、上書きされません", + "dkim_domain_or_sel_invalid": "DKIMドメインまたはセレクタが無効です: %s", + "domain_cannot_match_hostname": "ドメインはホスト名と一致してはなりません", + "domain_exists": "ドメイン %s は既に存在します", + "domain_invalid": "ドメイン名が空か無効です", + "domain_not_empty": "非空のドメイン %s を削除することはできません", + "domain_not_found": "ドメイン %s が見つかりません", + "domain_quota_m_in_use": "ドメインクォータは %s MiB 以上である必要があります", + "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": "グローバルマップID %s は無効です", + "global_map_write_error": "グローバルマップID %s を書き込めません: %s", + "goto_empty": "エイリアスアドレスには少なくとも1つの有効な転送先アドレスが含まれている必要があります", + "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\" は無効です", + "invalid_filter_type": "無効なフィルタタイプ", + "invalid_host": "無効なホストが指定されました: %s", + "invalid_mime_type": "無効なMIMEタイプ", + "invalid_nexthop": "次のホップフォーマットが無効です", + "invalid_nexthop_authenticated": "異なる認証情報で次のホップが存在します。最初にこの次のホップの既存の認証情報を更新してください。", + "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 は既にエイリアス、メールボックス、またはエイリアスドメインから展開されたエイリアスアドレスとして登録されています。", + "is_spam_alias": "%s は既に一時的なエイリアスアドレス(スパムエイリアスアドレス)として登録されています", + "last_key": "最後のキーを削除することはできません。代わりにTFAを無効化してください。", + "login_failed": "ログインに失敗しました", + "mailbox_defquota_exceeds_mailbox_maxquota": "デフォルトクォータが最大クォータ制限を超えています", + "mailbox_invalid": "メールボックス名が無効です", + "mailbox_quota_exceeded": "クォータがドメイン制限を超えています(最大 %d MiB)", + "mailbox_quota_exceeds_domain_quota": "最大クォータがドメインクォータ制限を超えています", + "mailbox_quota_left_exceeded": "残り容量が不足しています(残り容量: %d MiB)", + "mailboxes_in_use": "最大メールボックス数は %d 以上である必要があります", + "malformed_username": "ユーザー名が不正な形式です", + "map_content_empty": "マップの内容を空にすることはできません", + "max_alias_exceeded": "最大エイリアス数を超えています", + "max_mailbox_exceeded": "最大メールボックス数を超えています(%d / %d)", + "max_quota_in_use": "メールボックスクォータは %d MiB 以上である必要があります", + "maxquota_empty": "メールボックスごとの最大クォータを0にすることはできません。", + "mysql_error": "MySQLエラー: %s", + "network_host_invalid": "無効なネットワークまたはホスト: %s", + "next_hop_interferes": "%s は次のホップ %s と干渉しています", + "next_hop_interferes_any": "既存の次のホップが %s と干渉しています", + "nginx_reload_failed": "Nginxのリロードに失敗しました: %s", + "no_user_defined": "ユーザーが定義されていません", + "object_exists": "オブジェクト %s は既に存在します", + "object_is_not_numeric": "値 %s は数値ではありません", + "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", + "pushover_credentials_missing": "Pushoverトークンまたはキーが不足しています", + "pushover_key": "Pushoverキーの形式が間違っています", + "pushover_token": "Pushoverトークンの形式が間違っています", + "quota_not_0_not_numeric": "クォータは数値で >= 0 である必要があります", + "recipient_map_entry_exists": "受信者マップエントリ \"%s\" が存在します", + "recovery_email_failed": "リカバリーメールを送信できませんでした。管理者にお問い合わせください。", + "redis_error": "Redisエラー: %s", + "relayhost_invalid": "マップエントリ %s は無効です", + "release_send_failed": "メッセージをリリースできませんでした: %s", + "reset_f2b_regex": "正規表現フィルターをタイムリーにリセットできませんでした。再試行するか、数秒待ってからウェブサイトをリロードしてください。", + "reset_token_limit_exceeded": "リセットトークン制限を超えました。後でもう一度試してください。", + "resource_invalid": "リソース名 %s は無効です", + "rl_timeframe": "レート制限の時間枠が正しくありません", + "rspamd_ui_pw_length": "Rspamd UIのパスワードは6文字以上である必要があります", + "script_empty": "スクリプトを空にすることはできません", + "sender_acl_invalid": "送信者ACL値 %s は無効です", + "set_acl_failed": "ACLの設定に失敗しました", + "settings_map_invalid": "設定マップID %s は無効です", + "sieve_error": "Sieveパーサーエラー: %s", + "spam_learn_error": "スパム学習エラー: %s", + "subject_empty": "件名を空にすることはできません", + "target_domain_invalid": "ターゲットドメイン %s は無効です", + "targetd_not_found": "ターゲットドメイン %s が見つかりません", + "targetd_relay_domain": "ターゲットドメイン %s はリレードメインです", + "template_exists": "テンプレート %s は既に存在します", + "template_id_invalid": "テンプレートID %s は無効です", + "template_name_invalid": "テンプレート名が無効です", + "temp_error": "一時的なエラー", + "text_empty": "テキストを空にすることはできません", + "tfa_token_invalid": "TFAトークンが無効です", + "tls_policy_map_dest_invalid": "ポリシーの宛先が無効です", + "tls_policy_map_entry_exists": "TLSポリシーマップエントリ \"%s\" が存在します", + "tls_policy_map_parameter_invalid": "ポリシーパラメータが無効です", + "to_invalid": "受信者を空にすることはできません", + "totp_verification_failed": "TOTP認証に失敗しました", + "transport_dest_exists": "トランスポートの宛先 \"%s\" が存在します", + "webauthn_verification_failed": "WebAuthn認証に失敗しました: %s", + "webauthn_authenticator_failed": "選択された認証器が見つかりませんでした", + "webauthn_publickey_failed": "選択された認証器に公開鍵が保存されていません", + "webauthn_username_failed": "選択された認証器は別のアカウントに属しています", + "unknown": "不明なエラーが発生しました", + "unknown_tfa_method": "未知のTFA方法", + "unlimited_quota_acl": "ACLによって無制限クォータは禁止されています", + "username_invalid": "ユーザー名 %s は使用できません", + "validity_missing": "有効期限を設定してください", + "value_missing": "すべての値を入力してください", + "yotp_verification_failed": "Yubico OTP認証に失敗しました: %s" + }, + "datatables": { + "collapse_all": "すべて折りたたむ", + "decimal": ".", + "emptyTable": "テーブルにデータがありません", + "expand_all": "すべて展開する", + "info": "_TOTAL_ 件中 _START_ から _END_ を表示中", + "infoEmpty": "0 件中 0 から 0 を表示中", + "infoFiltered": "(_MAX_ 件のデータから絞り込み)", + "infoPostFix": "", + "thousands": ",", + "lengthMenu": "_MENU_ 件を表示", + "loadingRecords": "読み込み中...", + "processing": "お待ちください...", + "search": "検索:", + "zeroRecords": "一致する記録が見つかりません", + "paginate": { + "first": "最初", + "last": "最後", + "next": "次へ", + "previous": "前へ" + }, + "aria": { + "sortAscending": ": 昇順に並べ替えるには有効化", + "sortDescending": ": 降順に並べ替えるには有効化" + } + }, + "debug": { + "architecture": "アーキテクチャ", + "chart_this_server": "チャート(このサーバー)", + "containers_info": "コンテナ情報", + "container_running": "稼働中", + "container_disabled": "コンテナ停止または無効", + "container_stopped": "停止", + "cores": "コア数", + "current_time": "システム時間", + "disk_usage": "ディスク使用量", + "docs": "ドキュメント", + "error_show_ip": "パブリックIPアドレスを解決できませんでした", + "external_logs": "外部ログ", + "history_all_servers": "履歴(すべてのサーバー)", + "in_memory_logs": "インメモリーログ", + "jvm_memory_solr": "JVMメモリ使用量", + "last_modified": "最終更新日時", + "log_info": "

mailcowのインメモリーログはRedisリストに収集され、ハンマリングを軽減するために1分ごとにLOG_LINES (%d)にトリムされます。\r\n
インメモリーログは永続化を目的としたものではありません。インメモリーログを記録するすべてのアプリケーションは、Dockerデーモンとデフォルトのログドライバーにもログを記録します。\r\n
インメモリーログタイプは、コンテナの軽微な問題をデバッグするために使用してください。

\r\n

外部ログは指定されたアプリケーションのAPIを介して収集されます。

\r\n

静的ログは主にアクティビティログであり、Dockerdには記録されませんが(APIログを除く)、永続化が必要です。

", + "login_time": "ログイン時間", + "logs": "ログ", + "memory": "メモリ", + "online_users": "オンラインユーザー", + "restart_container": "再起動", + "service": "サービス", + "show_ip": "パブリックIPを表示", + "size": "サイズ", + "solr_dead": "Solrは起動中、無効化、または停止しました。", + "solr_status": "Solrのステータス", + "started_at": "開始時刻", + "started_on": "開始日", + "static_logs": "静的ログ", + "success": "成功", + "system_containers": "システムとコンテナ", + "timezone": "タイムゾーン", + "uptime": "稼働時間", + "update_available": "利用可能なアップデートがあります", + "no_update_available": "システムは最新バージョンです", + "update_failed": "アップデートの確認に失敗しました", + "username": "ユーザー名", + "wip": "作業中" + }, + "diagnostics": { + "cname_from_a": "A/AAAAレコードから派生した値です。この値は、レコードが正しいリソースを指している限りサポートされています。", + "dns_records": "DNSレコード", + "dns_records_24hours": "DNSに加えた変更が正しく反映されるまで最大24時間かかる場合があります。このページは、DNSレコードの設定方法を簡単に確認したり、すべてのレコードがDNSに正しく保存されているかを確認するためのものです。", + "dns_records_data": "正しいデータ", + "dns_records_docs": "ドキュメントも参照してください。", + "dns_records_name": "名前", + "dns_records_status": "現在の状態", + "dns_records_type": "タイプ", + "optional": "このレコードは任意です。" + }, + "edit": { + "acl": "ACL(権限)", + "active": "アクティブ", + "admin": "管理者を編集", + "advanced_settings": "詳細設定", + "alias": "エイリアスを編集", + "allow_from_smtp": "SMTPを使用するこれらのIPのみを許可", + "allow_from_smtp_info": "すべての送信者を許可するには空欄にしてください。
IPv4/IPv6アドレスおよびネットワークを指定できます。", + "allowed_protocols": "許可されたプロトコル", + "app_name": "アプリ名", + "app_passwd": "アプリパスワード", + "app_passwd_protocols": "アプリパスワードで許可されるプロトコル", + "automap": "フォルダーを自動マッピングする(例:\"Sent items\", \"Sent\" => \"Sent\" など)", + "backup_mx_options": "リレーオプション", + "bcc_dest_format": "BCC送信先は1つの有効なメールアドレスである必要があります。
複数のアドレスにコピーを送信する必要がある場合は、エイリアスを作成してここで使用してください。", + "client_id": "クライアントID", + "client_secret": "クライアントシークレット", + "comment_info": "プライベートコメントはユーザーには表示されませんが、パブリックコメントはユーザー概要でホバーするとツールチップとして表示されます。", + "created_on": "作成日時", + "custom_attributes": "カスタム属性", + "delete1": "完了時にソースから削除", + "delete2": "ソースに存在しないメッセージを送信先で削除", + "delete2duplicates": "送信先で重複を削除", + "delete_ays": "削除プロセスを確認してください。", + "description": "説明", + "disable_login": "ログインを禁止(受信メールは許可)", + "domain": "ドメインを編集", + "domain_admin": "ドメイン管理者を編集", + "domain_footer": "ドメイン全体のフッター", + "domain_footer_html": "HTMLフッター", + "domain_footer_info": "ドメイン全体のフッターは、このドメイン内のアドレスに関連付けられたすべての送信メールに追加されます。
フッターで使用できる変数は以下の通りです:", + "domain_footer_info_vars": { + "auth_user": "{= auth_user =} - MTAによって指定された認証済みユーザー名", + "from_user": "{= from_user =} - エンベロープの送信者部分。例:「moo@mailcow.tld」の場合、「moo」を返します。", + "from_name": "{= from_name =} - エンベロープの送信者名。例:「Mailcow 」の場合、「Mailcow」を返します。", + "from_addr": "{= from_addr =} - エンベロープの送信者アドレス部分", + "from_domain": "{= from_domain =} - エンベロープの送信者ドメイン部分", + "custom": "{= foo =} - メールボックスに「foo」というカスタム属性があり、その値が「bar」の場合、「bar」を返します。" + }, + "domain_footer_plain": "プレインテキストのフッター", + "domain_footer_skip_replies": "返信メールでフッターを無視", + "domain_quota": "ドメインクォータ", + "domains": "ドメイン", + "dont_check_sender_acl": "ドメイン %s(およびエイリアスドメイン)の送信者チェックを無効化", + "edit_alias_domain": "エイリアスドメインを編集", + "encryption": "暗号化", + "exclude": "オブジェクトを除外(正規表現)", + "extended_sender_acl": "外部送信者アドレス", + "extended_sender_acl_info": "可能であればDKIMドメインキーをインポートしてください。
\r\n このサーバーを対応するSPF TXTレコードに追加することを忘れないでください。
\r\n このサーバーに外部アドレスと重複するドメインまたはエイリアスドメインを追加するたびに、外部アドレスは削除されます。
\r\n *@domain.tld として送信を許可するには @domain.tld を使用してください。", + "force_pw_update": "次回ログイン時にパスワードの更新を強制", + "force_pw_update_info": "このユーザーは %s にのみログインできます。アプリパスワードは引き続き使用可能です。", + "footer_exclude": "フッターから除外", + "full_name": "フルネーム", + "gal": "グローバルアドレスリスト", + "gal_info": "GALにはドメインのすべてのオブジェクトが含まれ、ユーザーは編集できません。無効にすると、SOGoの空き/取り込み情報が欠落します!変更を適用するにはSOGoを再起動してください。", + "generate": "生成", + "grant_types": "付与タイプ", + "hostname": "ホスト名", + "inactive": "非アクティブ", + "kind": "種類", + "last_modified": "最終更新日時", + "lookup_mx": "宛先がMX名に一致する正規表現です(例:.*\\.google\\.com は google.com で終わるMX宛のすべてのメールをこのホップにルートします)", + "mailbox": "メールボックスを編集", + "mailbox_quota_def": "デフォルトのメールボックスクォータ", + "mailbox_relayhost_info": "メールボックスおよび直接エイリアスにのみ適用され、ドメインリレーホストを上書きします。", + "mailbox_rename": "メールボックス名を変更", + "mailbox_rename_agree": "バックアップを作成しました。", + "mailbox_rename_warning": "重要!メールボックス名を変更する前にバックアップを作成してください。", + "mailbox_rename_alias": "エイリアスを自動作成", + "mailbox_rename_title": "新しいローカルメールボックス名", + "max_aliases": "最大エイリアス数", + "max_mailboxes": "作成可能な最大メールボックス数", + "max_quota": "メールボックスごとの最大クォータ(MiB)", + "maxage": "リモートから取得するメッセージの最大日数
(0 = 日数を無視)", + "maxbytespersecond": "毎秒の最大バイト数
(0 = 無制限)", + "mbox_rl_info": "このレート制限はSASLログイン名に適用され、ログインしたユーザーによって使用されるすべての「送信者」アドレスに一致します。メールボックスレート制限は、ドメイン全体のレート制限を上書きします。", + "mins_interval": "間隔(分)", + "multiple_bookings": "複数予約", + "none_inherit": "なし / 継承", + "nexthop": "次のホップ", + "password": "パスワード", + "password_recovery_email": "パスワードリカバリメール", + "password_repeat": "確認用パスワード(再入力)", + "previous": "前のページ", + "private_comment": "プライベートコメント", + "public_comment": "パブリックコメント", + "pushover": "Pushover", + "pushover_evaluate_x_prio": "高優先度メールをエスカレーションする [X-Priority: 1]", + "pushover_info": "プッシュ通知設定は%sに配信されるすべてのクリーン(スパムでない)メールに適用されます。エイリアス(共有、非共有、タグ付き)も含まれます。", + "pushover_only_x_prio": "高優先度メールのみを考慮 [X-Priority: 1]", + "pushover_sender_array": "以下の送信者メールアドレスのみを考慮(カンマ区切り)", + "pushover_sender_regex": "以下の送信者正規表現を考慮", + "pushover_text": "通知テキスト", + "pushover_title": "通知タイトル", + "pushover_sound": "通知音", + "pushover_vars": "送信者フィルターが定義されていない場合、すべてのメールが考慮されます。
正規表現フィルターおよび正確な送信者チェックを個別に定義可能で、順次考慮されます。これらは互いに依存しません。
テキストおよびタイトルに使用可能な変数(データ保護ポリシーに注意してください)", + "pushover_verify": "資格情報を確認", + "quota_mb": "クォータ(MiB)", + "quota_warning_bcc": "クォータ警告BCC", + "quota_warning_bcc_info": "警告は別のコピーとして以下の受信者に送信されます。件名には対応するユーザー名が括弧内に付加されます(例:Quota warning (user@example.com))。", + "ratelimit": "レート制限", + "redirect_uri": "リダイレクト/コールバックURL", + "relay_all": "すべての受信者をリレー", + "relay_all_info": "↪ すべての受信者をリレーしない場合、リレーする必要のある各受信者のために(\"ブラインド\")メールボックスを追加する必要があります。", + "relay_domain": "このドメインをリレー", + "relay_transport_info": "
情報
このドメインのカスタム宛先のトランスポートマップを定義できます。設定されていない場合、MXルックアップが実行されます。", + "relay_unknown_only": "存在しないメールボックスのみをリレー。存在するメールボックスはローカルに配信されます。", + "relayhost": "送信者依存のトランスポート", + "remove": "削除", + "resource": "リソース", + "save": "変更を保存", + "scope": "スコープ", + "sender_acl": "送信を許可するアドレス", + "sender_acl_disabled": "送信者チェックが無効です", + "sender_acl_info": "メールボックスユーザーAがメールボックスユーザーBとして送信することを許可されている場合、SOGoで送信者アドレスが自動的に選択可能な「送信者」フィールドとして表示されることはありません。
\r\n メールボックスユーザーBは、SOGoでメールボックスを委任する必要があります。この委任は、メールビューの左上にあるメールボックス名の右にあるメニュー(三点ボタン)を使用して行います。この動作はエイリアスアドレスには適用されません。", + "sieve_desc": "簡単な説明", + "sieve_type": "フィルタータイプ", + "skipcrossduplicates": "フォルダー間で重複するメッセージをスキップ(先着順)", + "sogo_access": "SOGoへの直接ログインアクセスを許可", + "sogo_access_info": "メールUI内からのシングルサインオンは引き続き動作します。この設定は他のすべてのサービスへのアクセスには影響しません。また、ユーザーの既存のSOGoプロファイルを削除または変更するものでもありません。", + "sogo_visible": "SOGoにエイリアスが表示される", + "sogo_visible_info": "このオプションは、SOGoで表示可能なオブジェクト(ローカルメールボックスを指す共有または非共有のエイリアスアドレス)にのみ影響します。非表示にすると、SOGoで選択可能な送信者としてエイリアスは表示されません。", + "spam_alias": "時間制限付きエイリアスアドレスを作成または変更", + "spam_filter": "スパムフィルター", + "spam_policy": "ホワイトリスト/ブラックリストにアイテムを追加または削除", + "spam_score": "カスタムスパムスコアを設定", + "subfolder2": "送信先でサブフォルダーに同期
(空欄 = サブフォルダーを使用しない)", + "syncjob": "同期ジョブを編集", + "target_address": "転送先アドレス(カンマ区切り)", + "target_domain": "ターゲットドメイン", + "timeout1": "リモートホストへの接続タイムアウト", + "timeout2": "ローカルホストへの接続タイムアウト", + "title": "オブジェクトを編集", + "unchanged_if_empty": "変更がない場合は空欄にしてください", + "username": "ユーザー名", + "validate_save": "検証して保存" + }, + "fido2": { + "confirm": "確認", + "fido2_auth": "FIDO2でログイン", + "fido2_success": "デバイスが正常に登録されました", + "fido2_validation_failed": "検証に失敗しました", + "fn": "フレンドリーネーム", + "known_ids": "登録済みID", + "none": "無効", + "register_status": "登録状況", + "rename": "名前を変更", + "set_fido2": "FIDO2デバイスを登録", + "set_fido2_touchid": "Apple M1でTouch IDを登録", + "set_fn": "フレンドリーネームを設定", + "start_fido2_validation": "FIDO2検証を開始" + }, + "footer": { + "cancel": "キャンセル", + "confirm_delete": "削除の確認", + "delete_now": "今すぐ削除", + "delete_these_items": "次のオブジェクトIDに対する変更を確認してください", + "hibp_check": "haveibeenpwned.comで確認", + "hibp_nok": "一致しました!これは潜在的に危険なパスワードです!", + "hibp_ok": "一致は見つかりませんでした。", + "loading": "お待ちください...", + "nothing_selected": "選択されていません", + "restart_container": "コンテナを再起動", + "restart_container_info": "重要: 優雅な再起動には時間がかかる場合があります。完了するまでお待ちください。", + "restart_now": "今すぐ再起動", + "restarting_container": "コンテナを再起動中です。これには時間がかかる場合があります。" + }, + "header": { + "administration": "構成と詳細", + "apps": "アプリ", + "debug": "情報", + "email": "Eメール", + "mailcow_system": "システム", + "mailcow_config": "構成", + "quarantine": "隔離", + "restart_netfilter": "netfilterを再起動", + "restart_sogo": "SOGoを再起動", + "user_settings": "ユーザー設定" + }, + "info": { + "awaiting_tfa_confirmation": "TFA(二要素認証)の確認待ち", + "no_action": "適用可能なアクションはありません", + "session_expires": "セッションは約15秒で期限切れになります" + }, + "login": { + "back_to_mailcow": "mailcowに戻る", + "delayed": "ログインが %s 秒遅れました。", + "fido2_webauthn": "FIDO2/WebAuthnでログイン", + "forgot_password": "> パスワードをお忘れですか?", + "invalid_pass_reset_token": "パスワードリセットトークンが無効または期限切れです。
新しいパスワードリセットリンクをリクエストしてください。", + "login": "ログイン", + "mobileconfig_info": "リクエストされたApple接続プロファイルをダウンロードするには、メールボックスユーザーとしてログインしてください。", + "new_password": "新しいパスワード", + "new_password_confirm": "新しいパスワードを確認", + "other_logins": "キーでログイン", + "password": "パスワード", + "reset_password": "パスワードをリセット", + "request_reset_password": "パスワード変更をリクエスト", + "username": "ユーザー名" + }, + "mailbox": { + "action": "アクション", + "activate": "有効化", + "active": "有効", + "add": "追加", + "add_alias": "エイリアスを追加", + "add_alias_expand": "エイリアスドメインでエイリアスを展開", + "add_bcc_entry": "BCCマップを追加", + "add_domain": "ドメインを追加", + "add_domain_alias": "ドメインエイリアスを追加", + "add_domain_record_first": "まずドメインを追加してください", + "add_filter": "フィルタを追加", + "add_mailbox": "メールボックスを追加", + "add_recipient_map_entry": "受信者マップを追加", + "add_resource": "リソースを追加", + "add_template": "テンプレートを追加", + "add_tls_policy_map": "TLSポリシーマップを追加", + "address_rewriting": "アドレス書き換え", + "alias": "エイリアス", + "alias_domain_alias_hint": "エイリアスはドメインエイリアスに自動的には適用されません。エイリアスアドレスmy-alias@domainは、アドレスmy-alias@alias-domainカバーしません(\"alias-domain\"は\"domain\"の仮想エイリアスドメイン)。
外部メールボックスにメールを転送するには、シーブフィルタを使用してください(\"Filters\"タブまたはSOGoのForwarderを使用)。\"エイリアスドメインでエイリアスを展開\"を使用して不足しているエイリアスを自動的に追加できます。", + "alias_domain_backupmx": "リレードメイン用にエイリアスドメインが無効", + "aliases": "エイリアス", + "all_domains": "すべてのドメイン", + "allow_from_smtp": "SMTPを使用するこれらのIPのみを許可", + "allow_from_smtp_info": "すべての送信者を許可するには空欄にしてください。
IPv4/IPv6アドレスおよびネットワークを指定可能。", + "allowed_protocols": "直接ユーザーアクセスで許可されるプロトコル(アプリパスワードプロトコルには影響しません)", + "backup_mx": "リレードメイン", + "bcc": "BCC", + "bcc_destination": "BCC送信先", + "bcc_destinations": "BCC送信先", + "bcc_info": "BCCマップはすべてのメッセージのコピーを他のアドレスに静かに転送するために使用されます。ローカル送信先がメールの受信者として機能するときに受信者マップタイプのエントリが使用されます。送信者マップも同じ原則に従います。
\r\n ローカル送信先は配送失敗について通知されません。", + "bcc_local_dest": "ローカル送信先", + "bcc_map": "BCCマップ", + "bcc_map_type": "BCCタイプ", + "bcc_maps": "BCCマップ", + "bcc_rcpt_map": "受信者マップ", + "bcc_sender_map": "送信者マップ", + "bcc_to_rcpt": "受信者マップタイプに切り替え", + "bcc_to_sender": "送信者マップタイプに切り替え", + "bcc_type": "BCCタイプ", + "booking_null": "常に空きとして表示", + "booking_0_short": "常に空き", + "booking_custom": "特定の予約数に制限", + "booking_custom_short": "ハードリミット", + "booking_ltnull": "無制限だが予約時は使用中として表示", + "booking_lt0_short": "ソフトリミット", + "catch_all": "キャッチオール", + "created_on": "作成日", + "daily": "毎日", + "deactivate": "無効化", + "description": "説明", + "disable_login": "ログインを禁止(受信メールは許可)", + "disable_x": "無効化", + "dkim_domains_selector": "セレクタ", + "dkim_key_length": "DKIMキーの長さ(ビット)", + "domain": "ドメイン", + "domain_admins": "ドメイン管理者", + "domain_aliases": "ドメインエイリアス", + "domain_templates": "ドメインテンプレート", + "domain_quota": "クォータ", + "domain_quota_total": "ドメイン全体のクォータ", + "domains": "ドメイン", + "edit": "編集", + "empty": "結果なし", + "enable_x": "有効化", + "excludes": "除外", + "filter_table": "フィルタテーブル", + "filters": "フィルタ", + "fname": "フルネーム", + "force_pw_update": "次回ログイン時にパスワード更新を強制", + "gal": "グローバルアドレスリスト", + "goto_ham": "Hamとして学習", + "goto_spam": "Spamとして学習", + "hourly": "毎時", + "in_use": "使用中 (%)", + "inactive": "非アクティブ", + "insert_preset": "例のプリセット \"%s\" を挿入", + "kind": "種類", + "last_mail_login": "最後のメールログイン", + "last_modified": "最終更新日", + "last_pw_change": "最後のパスワード変更", + "last_run": "最後の実行", + "last_run_reset": "次回のスケジュール設定", + "mailbox": "メールボックス", + "mailbox_defaults": "デフォルト設定", + "mailbox_defaults_info": "新しいメールボックスのデフォルト設定を定義します。", + "mailbox_defquota": "デフォルトのメールボックスサイズ", + "mailbox_templates": "メールボックステンプレート", + "mailbox_quota": "メールボックスの最大サイズ", + "mailboxes": "メールボックス", + "max_aliases": "最大エイリアス数", + "max_mailboxes": "作成可能な最大メールボックス数", + "max_quota": "メールボックスごとの最大クォータ", + "mins_interval": "間隔(分)", + "msg_num": "メッセージ番号", + "multiple_bookings": "複数予約", + "never": "なし", + "no": "✕", + "no_record": "オブジェクト %s に記録がありません", + "no_record_single": "記録なし", + "open_logs": "ログを開く", + "owner": "所有者", + "private_comment": "プライベートコメント", + "public_comment": "パブリックコメント", + "q_add_header": "迷惑メールフォルダに移動時", + "q_all": "迷惑メールフォルダに移動時および拒否時", + "q_reject": "拒否時", + "quarantine_category": "隔離通知カテゴリ", + "quarantine_notification": "隔離通知", + "quick_actions": "アクション", + "recipient": "受信者", + "recipient_map": "受信者マップ", + "recipient_map_info": "受信者マップは、メッセージが配信される前に宛先アドレスを置き換えるために使用されます。", + "recipient_map_new": "新しい受信者", + "recipient_map_new_info": "受信者マップの宛先は有効なメールアドレスである必要があります。", + "recipient_map_old": "元の受信者", + "recipient_map_old_info": "受信者マップの元の宛先は有効なメールアドレスまたはドメイン名である必要があります。", + "recipient_maps": "受信者マップ", + "relay_all": "すべての受信者をリレー", + "relay_unknown": "未知のメールボックスをリレー", + "remove": "削除", + "resources": "リソース", + "running": "稼働中", + "sender": "送信者", + "set_postfilter": "ポストフィルターとして設定", + "set_prefilter": "プリフィルターとして設定", + "sieve_info": "ユーザーごとに複数のフィルタを保存できますが、同時にアクティブにできるのは1つのプリフィルタと1つのポストフィルタだけです。
\r\n各フィルタは指定された順序で処理されます。スクリプトの失敗や\"keep;\"の発行は、後続のスクリプトの処理を停止しません。グローバルシーブスクリプトの変更はDovecotの再起動をトリガーします。

グローバルシーブプリフィルタ • プリフィルタ • ユーザースクリプト • ポストフィルタ • グローバルシーブポストフィルタ", + "sieve_preset_1": "危険性の高いファイルタイプのメールを破棄", + "sieve_preset_2": "特定の送信者のメールを常に既読としてマーク", + "sieve_preset_3": "静かに破棄し、すべてのシーブ処理を停止", + "sieve_preset_4": "INBOXにファイルし、シーブフィルタによるさらなる処理をスキップ", + "sieve_preset_5": "自動返信(バケーション)", + "sieve_preset_6": "応答付きでメールを拒否", + "sieve_preset_7": "リダイレクトして保持/削除", + "sieve_preset_8": "特定の送信者からのメールをリダイレクトし、既読としてマークしてサブフォルダーに分類", + "sieve_preset_header": "以下の例のプリセットをご覧ください。詳細についてはWikipediaを参照してください。", + "sogo_visible": "エイリアスはSOGoで表示されます", + "sogo_visible_n": "SOGoでエイリアスを非表示", + "sogo_visible_y": "SOGoでエイリアスを表示", + "spam_aliases": "一時的なエイリアス", + "stats": "統計", + "status": "ステータス", + "sync_jobs": "同期ジョブ", + "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": "ターゲットドメイン", + "templates": "テンプレート", + "template": "テンプレート", + "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_policy": "ポリシー", + "tls_policy_maps": "TLSポリシーマップ", + "tls_policy_maps_enforced_tls": "これらのポリシーは、送信TLS接続を強制するメールボックスユーザーの動作も上書きします。以下にポリシーが存在しない場合、これらのユーザーはデフォルト値(smtp_tls_mandatory_protocolsおよびsmtp_tls_mandatory_ciphers)を適用します。", + "tls_policy_maps_info": "このポリシーマップは、ユーザーのTLSポリシー設定とは無関係に送信TLSトランスポートルールを上書きします。
\r\n 詳細については\"smtp_tls_policy_maps\"のドキュメントをご確認ください。", + "tls_policy_maps_long": "送信TLSポリシーマップの上書き", + "toggle_all": "すべて切り替え", + "username": "ユーザー名", + "waiting": "待機中", + "weekly": "毎週", + "yes": "✓" + }, + "oauth2": { + "access_denied": "OAuth2経由でアクセスを許可するには、メールボックスの所有者としてログインしてください。", + "authorize_app": "アプリケーションを認証", + "deny": "拒否", + "permit": "アプリケーションを許可", + "profile": "プロファイル", + "profile_desc": "個人情報を表示: ユーザー名、フルネーム、作成日、更新日、アクティブ状態", + "scope_ask_permission": "以下の権限を要求するアプリケーションがあります" + }, + "quarantine": { + "action": "アクション", + "atts": "添付ファイル", + "check_hash": "ファイルハッシュをVTで検索", + "confirm": "確認", + "confirm_delete": "この要素の削除を確認してください。", + "danger": "危険", + "deliver_inbox": "受信トレイに配信", + "disabled_by_config": "現在のシステム設定では隔離機能が無効になっています。「メールボックスごとの保存期間」と「隔離要素の最大サイズ」を設定してください。", + "download_eml": "ダウンロード(.eml)", + "empty": "結果がありません", + "high_danger": "高", + "info": "情報", + "junk_folder": "迷惑メールフォルダ", + "learn_spam_delete": "スパムとして学習して削除", + "low_danger": "低", + "medium_danger": "中", + "neutral_danger": "中立", + "notified": "通知済み", + "qhandler_success": "リクエストがシステムに正常に送信されました。このウィンドウを閉じることができます。", + "qid": "Rspamd QID", + "qinfo": "隔離システムは拒否されたメールをデータベースに保存します(送信者には配信されたようには見えません)。また、メールボックスの迷惑メールフォルダにコピーとして配信されたメールも保存します。\r\n
「スパムとして学習して削除」は、ベイズ理論を使用してメッセージをスパムとして学習し、将来的に類似メッセージを拒否するためのファジーハッシュを計算します。\r\n
複数のメッセージを学習することは、システムによっては時間がかかる場合があります。
ブラックリスト登録された要素は隔離対象外です。", + "qitem": "隔離項目", + "quarantine": "隔離", + "quick_actions": "クイックアクション", + "quick_delete_link": "クイック削除リンクを開く", + "quick_info_link": "情報リンクを開く", + "quick_release_link": "クイックリリースリンクを開く", + "rcpt": "受信者", + "received": "受信日時", + "recipients": "受信者", + "refresh": "更新", + "rejected": "拒否", + "release": "リリース", + "release_body": "メッセージを.emlファイルとして添付しました。", + "release_subject": "潜在的に危険な隔離項目 %s", + "remove": "削除", + "rewrite_subject": "件名の書き換え", + "rspamd_result": "Rspamd 結果", + "sender": "送信者(SMTP)", + "sender_header": "送信者(「From」ヘッダー)", + "settings_info": "隔離対象の要素数の最大値: %s
最大メールサイズ: %s MiB", + "show_item": "項目を表示", + "spam": "スパム", + "spam_score": "スコア", + "subj": "件名", + "table_size": "テーブルサイズ", + "table_size_show_n": "%s 件表示", + "text_from_html_content": "コンテンツ(変換されたHTML)", + "text_plain_content": "コンテンツ(text/plain)", + "toggle_all": "すべて選択/解除", + "type": "タイプ" + }, + "queue": { + "delete": "すべて削除", + "flush": "キューをフラッシュ", + "info": "メールキューには、配信を待っているすべてのメールが含まれています。メールが長時間メールキューに停滞している場合、システムによって自動的に削除されます。
該当メールのエラーメッセージは、メールが配信できなかった理由についての情報を提供します。", + "legend": "メールキュー操作機能:", + "ays": "現在のキューからすべてのアイテムを削除することを確認してください。", + "deliver_mail": "配信", + "deliver_mail_legend": "選択されたメールの再配信を試みます。", + "hold_mail": "保留", + "hold_mail_legend": "選択されたメールを保留します。(さらに配信を試みるのを防ぎます)", + "queue_manager": "キューマネージャー", + "show_message": "メッセージを表示", + "unban": "キューの禁止解除", + "unhold_mail": "保留解除", + "unhold_mail_legend": "配信のために選択されたメールを解放します。(事前に保留が必要)" + }, + "ratelimit": { + "disabled": "無効", + "second": "通数 / 秒", + "minute": "通数 / 分", + "hour": "通数 / 時間", + "day": "通数 / 日" + }, + "start": { + "help": "ヘルプパネルを表示/非表示", + "imap_smtp_server_auth_info": "メールアドレス全体を使用し、PLAIN認証メカニズムを使用してください。
\r\nログインデータはサーバー側の強制暗号化により保護されます。", + "mailcow_apps_detail": "mailcowアプリを使用して、メール、カレンダー、連絡先などにアクセスできます。", + "mailcow_panel_detail": "ドメイン管理者は、メールボックスやエイリアスを作成、変更、削除したり、ドメインを変更したり、割り当てられたドメインに関する情報を確認できます。
\r\nメールボックスユーザーは、期間限定のエイリアス(スパムエイリアス)を作成したり、パスワードやスパムフィルターの設定を変更したりできます。" + }, + "success": { + "acl_saved": "オブジェクト %s のACLを保存しました", + "admin_added": "管理者 %s が追加されました", + "admin_api_modified": "APIの変更が保存されました", + "admin_modified": "管理者の変更が保存されました", + "admin_removed": "管理者 %s が削除されました", + "alias_added": "エイリアスアドレス %s (%d) が追加されました", + "alias_domain_removed": "エイリアスドメイン %s が削除されました", + "alias_modified": "エイリアスアドレス %s の変更が保存されました", + "alias_removed": "エイリアス %s が削除されました", + "aliasd_added": "エイリアスドメイン %s が追加されました", + "aliasd_modified": "エイリアスドメイン %s の変更が保存されました", + "app_links": "アプリリンクの変更を保存しました", + "app_passwd_added": "新しいアプリパスワードを追加しました", + "app_passwd_removed": "アプリパスワードID %s を削除しました", + "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", + "deleted_syncjob": "同期ジョブID %s を削除しました", + "deleted_syncjobs": "同期ジョブを削除しました: %s", + "dkim_added": "DKIMキー %s を保存しました", + "domain_add_dkim_available": "既にDKIMキーが存在していました", + "dkim_duplicated": "ドメイン %s のDKIMキーを %s にコピーしました", + "dkim_removed": "DKIMキー %s を削除しました", + "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": "ユーザー %s のActiveSyncデバイスをリセットしました", + "f2b_banlist_refreshed": "Banlist ID が正常に更新されました。", + "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 を正常に削除しました", + "items_released": "選択されたアイテムをリリースしました", + "learned_ham": "ID %s をhamとして正常に学習しました", + "license_modified": "ライセンスの変更が保存されました", + "logged_in_as": "%s としてログインしました", + "mailbox_added": "メールボックス %s を追加しました", + "mailbox_modified": "メールボックス %s の変更が保存されました", + "mailbox_removed": "メールボックス %s が削除されました", + "mailbox_renamed": "メールボックス名を %s から %s に変更しました", + "nginx_reloaded": "Nginxをリロードしました", + "object_modified": "オブジェクト %s の変更が保存されました", + "password_policy_saved": "パスワードポリシーが正常に保存されました", + "password_changed_success": "パスワードが正常に変更されました", + "pushover_settings_edited": "Pushover設定が正常に設定されました。資格情報を確認してください。", + "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": "メインロゴをデフォルトにリセットしました", + "resource_added": "リソース %s を追加しました", + "resource_modified": "メールボックス %s の変更が保存されました", + "resource_removed": "リソース %s を削除しました", + "rl_saved": "オブジェクト %s のレート制限を保存しました", + "rspamd_ui_pw_set": "Rspamd UIパスワードを正常に設定しました", + "saved_settings": "設定を保存しました", + "settings_map_added": "設定マップエントリを追加しました", + "settings_map_removed": "設定マップID %s を削除しました", + "sogo_profile_reset": "ユーザー %s のSOGoプロファイルをリセットしました", + "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テキストの変更を保存しました", + "upload_success": "ファイルを正常にアップロードしました", + "verified_fido2_login": "FIDO2ログインを確認しました", + "verified_totp_login": "TOTPログインを確認しました", + "verified_webauthn_login": "WebAuthnログインを確認しました", + "verified_yotp_login": "Yubico OTPログインを確認しました" + }, + "tfa": { + "authenticators": "認証アプリ", + "api_register": "%s は Yubico Cloud API を使用しています。こちらからAPIキーを取得してください。", + "confirm": "確認", + "confirm_totp_token": "生成されたトークンを入力して変更を確認してください。", + "delete_tfa": "TFAを無効化", + "disable_tfa": "次回の成功したログインまでTFAを無効化", + "enter_qr_code": "デバイスがQRコードをスキャンできない場合、以下のTOTPコードを入力してください。", + "error_code": "エラーコード", + "init_webauthn": "初期化中です。しばらくお待ちください...", + "key_id": "デバイスの識別子", + "key_id_totp": "キーの識別子", + "none": "無効化", + "reload_retry": "- (エラーが続く場合はブラウザを再読み込みしてください)", + "scan_qr_code": "以下のコードを認証アプリでスキャンするか、手動でコードを入力してください。", + "select": "選択してください", + "set_tfa": "二要素認証方法を設定", + "start_webauthn_validation": "検証を開始", + "tfa": "二要素認証", + "tfa_token_invalid": "TFAトークンが無効です", + "totp": "時間ベースOTP(Google Authenticator、Authyなど)", + "u2f_deprecated": "キーが非推奨のU2F方式で登録されているようです。二要素認証を無効化し、キーを削除します。", + "u2f_deprecated_important": "管理パネルで新しいWebAuthn方式を使用してキーを登録してください。", + "webauthn": "WebAuthn認証", + "waiting_usb_auth": "USBデバイスを待っています...

USBデバイスのボタンを押してください。", + "waiting_usb_register": "USBデバイスを待っています...

上記にパスワードを入力し、USBデバイスのボタンを押して登録を確認してください。", + "yubi_otp": "Yubico OTP認証" + }, + "user": { + "action": "アクション", + "active": "有効", + "active_sieve": "アクティブなフィルタ", + "advanced_settings": "詳細設定", + "alias": "エイリアス", + "alias_create_random": "ランダムなエイリアスを生成", + "alias_extend_all": "エイリアスを1時間延長", + "alias_full_date": "d.m.Y, H:i:s T", + "alias_remove_all": "すべてのエイリアスを削除", + "alias_select_validity": "有効期間", + "alias_time_left": "残り時間", + "alias_valid_until": "有効期限", + "aliases_also_send_as": "ユーザーとしての送信も許可", + "aliases_send_as_all": "以下のドメインおよびそのエイリアスドメインの送信者アクセスを確認しない", + "app_hint": "アプリパスワードは、IMAP、SMTP、CalDAV、CardDAV、EASのログイン用の代替パスワードです。ユーザー名は変更されません。SOGoウェブメールはアプリパスワードで利用できません。", + "allowed_protocols": "許可されたプロトコル", + "app_name": "アプリ名", + "app_passwds": "アプリパスワード", + "apple_connection_profile": "Apple接続プロファイル", + "apple_connection_profile_complete": "この接続プロファイルには、Appleデバイス向けのIMAPおよびSMTPパラメータ、CalDAV(カレンダー)、CardDAV(連絡先)のパスが含まれます。", + "apple_connection_profile_mailonly": "この接続プロファイルには、Appleデバイス向けのIMAPおよびSMTP設定パラメータが含まれます。", + "apple_connection_profile_with_app_password": "新しいアプリパスワードが生成され、プロファイルに追加されるため、デバイスのセットアップ時にパスワードを入力する必要はありません。このファイルを共有しないでください。メールボックスへの完全なアクセス権を与えます。", + "attribute": "属性", + "change_password": "パスワードを変更", + "change_password_hint_app_passwords": "アカウントには %d のアプリパスワードがあり、それらは変更されません。これらを管理するには、「アプリパスワード」タブに移動してください。", + "clear_recent_successful_connections": "最近の成功した接続をクリア", + "client_configuration": "メールクライアントとスマートフォンの設定ガイドを表示", + "create_app_passwd": "アプリパスワードを作成", + "create_syncjob": "新しい同期ジョブを作成", + "created_on": "作成日", + "daily": "毎日", + "day": "日", + "delete_ays": "削除プロセスを確認してください。", + "direct_aliases": "直接エイリアスアドレス", + "direct_aliases_desc": "直接エイリアスアドレスは、スパムフィルターおよびTLSポリシー設定の影響を受けます。", + "direct_protocol_access": "このメールボックスユーザーは以下のプロトコルおよびアプリケーションへの直接的かつ外部からのアクセスが可能です。この設定は管理者によって制御されます。アプリパスワードを作成することで、個々のプロトコルおよびアプリケーションへのアクセスを許可できます。
「ウェブメールにログイン」ボタンはSOGoへのシングルサインオンを提供し、常に利用可能です。", + "eas_reset": "ActiveSyncデバイスキャッシュをリセット", + "eas_reset_help": "多くの場合、デバイスキャッシュのリセットは壊れたActiveSyncプロファイルの回復に役立ちます。
注意: すべての要素が再ダウンロードされます!", + "eas_reset_now": "今すぐリセット", + "edit": "編集", + "email": "メール", + "email_and_dav": "メール、カレンダー、および連絡先", + "empty": "結果なし", + "encryption": "暗号化", + "excludes": "除外", + "expire_in": "有効期限まで", + "fido2_webauthn": "FIDO2/WebAuthn", + "force_pw_update": "グループウェア関連サービスにアクセスするには、新しいパスワードを必ず設定する必要があります。", + "from": "送信元", + "generate": "生成", + "hour": "時間", + "hourly": "毎時", + "hours": "時間", + "in_use": "使用中", + "interval": "間隔", + "is_catch_all": "ドメイン用キャッチオール", + "last_mail_login": "最後のメールログイン", + "last_pw_change": "最後のパスワード変更", + "last_run": "最後の実行", + "last_ui_login": "最後のUIログイン", + "loading": "読み込み中...", + "login_history": "ログイン履歴", + "mailbox": "メールボックス", + "mailbox_details": "詳細", + "mailbox_general": "一般", + "mailbox_settings": "設定", + "messages": "メッセージ", + "month": "月", + "months": "ヶ月", + "never": "なし", + "new_password": "新しいパスワード", + "new_password_repeat": "確認用パスワード(再入力)", + "no_active_filter": "利用可能なアクティブなフィルタがありません", + "no_last_login": "最後のUIログイン情報がありません", + "no_record": "記録なし", + "open_logs": "ログを開く", + "open_webmail_sso": "ウェブメールにログイン", + "password": "パスワード", + "password_now": "現在のパスワード(変更を確認)", + "password_repeat": "パスワード(再入力)", + "password_reset_info": "パスワードリカバリ用のメールが提供されていない場合、この機能は使用できません。", + "pushover_evaluate_x_prio": "高優先度メールをエスカレーション [X-Priority: 1]", + "pushover_info": "プッシュ通知設定は、%sに配信されるすべてのクリーン(スパムでない)メールに適用されます。エイリアス(共有、非共有、タグ付き)も含まれます。", + "pushover_only_x_prio": "高優先度メールのみを考慮 [X-Priority: 1]", + "pushover_sender_array": "以下の送信者メールアドレスを考慮(カンマ区切り)", + "pushover_sender_regex": "以下の正規表現で送信者を一致", + "pushover_text": "通知テキスト", + "pushover_title": "通知タイトル", + "pushover_sound": "通知音", + "pushover_vars": "送信者フィルターが定義されていない場合、すべてのメールが考慮されます。
正規表現フィルターおよび正確な送信者チェックを個別に定義可能で、順次考慮されます。これらは互いに依存しません。
テキストおよびタイトルに使用可能な変数(データ保護ポリシーに注意してください)", + "pushover_verify": "資格情報を確認", + "pw_recovery_email": "パスワードリカバリメール", + "q_add_header": "迷惑メールフォルダ", + "q_all": "すべてのカテゴリ", + "q_reject": "拒否された", + "quarantine_category": "隔離通知カテゴリ", + "quarantine_category_info": "通知カテゴリ\"拒否\"には拒否されたメールが含まれます。一方\"迷惑メールフォルダ\"は、迷惑メールフォルダに振り分けられたメールをユーザーに通知します。", + "quarantine_notification": "隔離通知", + "quarantine_notification_info": "通知が送信されると、アイテムは\"通知済み\"としてマークされ、該当アイテムに対してさらに通知は送信されません。", + "recent_successful_connections": "最近の成功した接続", + "remove": "削除", + "running": "稼働中", + "save": "変更を保存", + "save_changes": "変更を保存", + "sender_acl_disabled": "送信者チェックが無効です", + "shared_aliases": "共有エイリアスアドレス", + "shared_aliases_desc": "共有エイリアスは、スパムフィルターや暗号化ポリシーなどのユーザー固有の設定の影響を受けません。対応するスパムフィルターは、ドメイン全体のポリシーとして管理者のみが設定可能です。", + "show_sieve_filters": "アクティブなユーザーシーブフィルタを表示", + "sogo_profile_reset": "SOGoプロファイルをリセット", + "sogo_profile_reset_help": "これにより、ユーザーのSOGoプロファイルが破壊され、連絡先とカレンダーデータが完全に削除されます。", + "sogo_profile_reset_now": "今すぐプロファイルをリセット", + "spam_aliases": "一時メールエイリアス", + "spam_score_reset": "サーバーデフォルトにリセット", + "spamfilter": "スパムフィルター", + "spamfilter_behavior": "評価", + "spamfilter_bl": "ブラックリスト", + "spamfilter_bl_desc": "ブラックリストに登録されたメールアドレスは常にスパムとして分類され拒否されます。拒否されたメールは隔離されません。ワイルドカードを使用できます。フィルターは直接エイリアス(単一ターゲットメールボックスを持つエイリアス)にのみ適用され、キャッチオールエイリアスやメールボックス自体には適用されません。", + "spamfilter_default_score": "デフォルト値", + "spamfilter_green": "緑: このメッセージはスパムではありません", + "spamfilter_hint": "最初の値は「低スパムスコア」を表し、2番目の値は「高スパムスコア」を表します。", + "spamfilter_red": "赤: このメッセージはスパムであり、サーバーによって拒否されます", + "spamfilter_table_action": "アクション", + "spamfilter_table_add": "アイテムを追加", + "spamfilter_table_domain_policy": "該当なし(ドメインポリシー)", + "spamfilter_table_empty": "表示するデータがありません", + "spamfilter_table_remove": "削除", + "spamfilter_table_rule": "ルール", + "spamfilter_wl": "ホワイトリスト", + "spamfilter_wl_desc": "ホワイトリストに登録されたメールアドレスは絶対にスパムとして分類されません。ワイルドカードを使用できます。フィルターは直接エイリアス(単一ターゲットメールボックスを持つエイリアス)にのみ適用され、キャッチオールエイリアスやメールボックス自体には適用されません。", + "spamfilter_yellow": "黄: このメッセージはスパムの可能性があり、スパムとしてタグ付けされ、迷惑メールフォルダに移動されます", + "status": "ステータス", + "sync_jobs": "同期ジョブ", + "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": "タグ付きメールアドレスの例: me+Facebook@example.org", + "tag_help_explain": "サブフォルダー: タグ名に基づいて新しいサブフォルダーがINBOXの下に作成されます(例: \"INBOX/Facebook\")。
\r\n件名: タグ名がメールの件名に追加されます(例: \"[Facebook] My News\")。", + "tag_in_none": "何もしない", + "tag_in_subfolder": "サブフォルダーに移動", + "tag_in_subject": "件名に追加", + "text": "テキスト", + "title": "タイトル", + "tls_enforce_in": "TLS受信を強制", + "tls_enforce_out": "TLS送信を強制", + "tls_policy": "暗号化ポリシー", + "tls_policy_warning": "警告: 暗号化メール転送を強制する場合、メールを失う可能性があります。
ポリシーを満たさないメッセージは、メールシステムによってハードフェイルでバウンスされます。
このオプションは、プライマリメールアドレス(ログイン名)、エイリアスドメインから派生したすべてのアドレス、およびこの単一メールボックスのみをターゲットとするエイリアスアドレスに適用されます。", + "user_settings": "ユーザー設定", + "username": "ユーザー名", + "value": "値", + "verify": "確認", + "waiting": "待機中", + "week": "週", + "weekly": "毎週", + "weeks": "週", + "with_app_password": "アプリパスワードを使用", + "year": "年", + "years": "年" + }, + "warning": { + "cannot_delete_self": "ログイン中のユーザーは削除できません", + "domain_added_sogo_failed": "ドメインは追加されましたが、SOGoの再起動に失敗しました。サーバーログを確認してください。", + "dovecot_restart_failed": "Dovecotの再起動に失敗しました。ログを確認してください。", + "fuzzy_learn_error": "ファジーハッシュ学習エラー: %s", + "hash_not_found": "ハッシュが見つからないか、すでに削除されています", + "ip_invalid": "無効なIPをスキップしました: %s", + "is_not_primary_alias": "プライマリアイアスではないためスキップされました: %s", + "no_active_admin": "最後のアクティブな管理者を無効化することはできません", + "quota_exceeded_scope": "ドメインのクォータを超えています。このドメインスコープでは無制限のメールボックスのみ作成可能です。", + "session_token": "フォームトークンが無効です: トークンの不一致", + "session_ua": "フォームトークンが無効です: ユーザーエージェント検証エラー" + } +} From 8853e2c44ae87d285069d549e3f318ad4559d9cd Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Mon, 9 Dec 2024 09:50:16 +0100 Subject: [PATCH 19/52] [Nginx] Use SOGo IPv4 for upstream --- data/Dockerfiles/nginx/bootstrap.py | 5 +++-- data/Dockerfiles/nginx/docker-entrypoint.sh | 8 ++++---- docker-compose.yml | 8 ++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/data/Dockerfiles/nginx/bootstrap.py b/data/Dockerfiles/nginx/bootstrap.py index 0d24ae9b2..d47e6318a 100644 --- a/data/Dockerfiles/nginx/bootstrap.py +++ b/data/Dockerfiles/nginx/bootstrap.py @@ -20,8 +20,9 @@ def nginx_conf(env, template_vars): f.write(config) def prepare_template_vars(): + ipv4_network = os.getenv("IPV4_NETWORK", "172.22.1") template_vars = { - 'IPV4_NETWORK': os.getenv("IPV4_NETWORK", "172.22.1"), + 'IPV4_NETWORK': ipv4_network, '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"), @@ -30,7 +31,7 @@ def prepare_template_vars(): '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"), + 'SOGOHOST': os.getenv("SOGOHOST", ipv4_network + ".248"), 'RSPAMDHOST': os.getenv("RSPAMDHOST", "rspamd-mailcow"), 'PHPFPMHOST': os.getenv("PHPFPMHOST", "php-fpm-mailcow"), } diff --git a/data/Dockerfiles/nginx/docker-entrypoint.sh b/data/Dockerfiles/nginx/docker-entrypoint.sh index beb0b4d9e..ea2e048e9 100755 --- a/data/Dockerfiles/nginx/docker-entrypoint.sh +++ b/data/Dockerfiles/nginx/docker-entrypoint.sh @@ -1,9 +1,9 @@ #!/bin/sh -until ping ${REDISHOST} -c1 > /dev/null; do - echo "Waiting for Redis..." - sleep 1 -done +PHPFPMHOST=${PHPFPMHOST:-"php-fpm-mailcow"} +SOGOHOST=${SOGOHOST:-"$IPV4_NETWORK.248"} +RSPAMDHOST=${RSPAMDHOST:-"rspamd-mailcow"} + until ping ${PHPFPMHOST} -c1 > /dev/null; do echo "Waiting for PHP..." sleep 1 diff --git a/docker-compose.yml b/docker-compose.yml index 226c48ac8..f815ab9b4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -382,10 +382,10 @@ services: - TZ=${TZ} - SKIP_SOGO=${SKIP_SOGO:-n} - SKIP_RSPAMD=${SKIP_RSPAMD:-n} - - PHPFPMHOST=${PHPFPMHOST:-php-fpm-mailcow} - - SOGOHOST=${SOGOHOST:-sogo-mailcow} - - RSPAMDHOST=${RSPAMDHOST:-rspamd-mailcow} - - REDISHOST=${REDISHOST:-redis-mailcow} + - PHPFPMHOST=${PHPFPMHOST:-} + - SOGOHOST=${SOGOHOST:-} + - RSPAMDHOST=${RSPAMDHOST:-} + - REDISHOST=${REDISHOST:-} - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} volumes: - ./data/web:/web:ro,z From 49e05f512061400db89cdbe6e435898e6e27bdc4 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Mon, 9 Dec 2024 11:36:05 +0100 Subject: [PATCH 20/52] [Web] fix oauth2 redirect after login --- data/web/inc/triggers.inc.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data/web/inc/triggers.inc.php b/data/web/inc/triggers.inc.php index 34e47a544..360f57277 100644 --- a/data/web/inc/triggers.inc.php +++ b/data/web/inc/triggers.inc.php @@ -111,8 +111,10 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { header("Location: /mobileconfig.php"); die(); } - header("Location: /user"); - die(); + if (!isset($_SESSION['oauth2_request'])) { + header("Location: /user"); + die(); + } } elseif ($as != "pending") { unset($_SESSION['pending_mailcow_cc_username']); From fa3b789fbbe3843075a63c1f4bb9e3458f7829b6 Mon Sep 17 00:00:00 2001 From: FreddleSpl0it Date: Mon, 9 Dec 2024 13:07:00 +0100 Subject: [PATCH 21/52] [Web] fix issue #6185 --- data/web/templates/user.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/web/templates/user.twig b/data/web/templates/user.twig index 5536abe4e..bf244f742 100644 --- a/data/web/templates/user.twig +++ b/data/web/templates/user.twig @@ -15,7 +15,7 @@ {% if acl.spam_alias == 1 %} {% endif %} - {% if acl.spam_score == 1 %} + {% if acl.spam_score == 1 or acl.spam_policy == 1 %} {% endif %} {% if acl.syncjobs == 1 %} @@ -36,7 +36,7 @@ {% include 'user/tab-user-details.twig' %} {% include 'user/tab-user-settings.twig' %} {% if acl.spam_alias == 1 %}{% include 'user/SpamAliases.twig' %}{% endif %} - {% if acl.spam_score == 1 %}{% include 'user/Spamfilter.twig' %}{% endif %} + {% if acl.spam_score == 1 or acl.spam_policy == 1 %}{% include 'user/Spamfilter.twig' %}{% endif %} {% if acl.syncjobs == 1 %}{% include 'user/Syncjobs.twig' %}{% endif %} {% if acl.app_passwds == 1 %}{% include 'user/AppPasswds.twig' %}{% endif %} {% if acl.pushover == 1 %}{% include 'user/Pushover.twig' %}{% endif %} From ed2837edd800751447005d3a877d48899d6f17b7 Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 9 Dec 2024 13:49:24 +0100 Subject: [PATCH 22/52] Remove legacy Nextcloud settings (#6050) --- .github/renovate.json | 6 - data/Dockerfiles/phpfpm/Dockerfile | 2 +- data/assets/nextcloud/nextcloud.conf | 130 ------------- data/assets/nextcloud/occ | 2 - helper-scripts/nextcloud.sh | 264 --------------------------- 5 files changed, 1 insertion(+), 403 deletions(-) delete mode 100644 data/assets/nextcloud/nextcloud.conf delete mode 100755 data/assets/nextcloud/occ delete mode 100755 helper-scripts/nextcloud.sh diff --git a/.github/renovate.json b/.github/renovate.json index 62cbf93fd..08f87ea37 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -15,12 +15,6 @@ "data\/web\/inc\/lib\/vendor\/**" ], "regexManagers": [ - { - "fileMatch": ["^helper-scripts\/nextcloud.sh$"], - "matchStrings": [ - "#\\srenovate:\\sdatasource=(?.*?) depName=(?.*?)( versioning=(?.*?))?( extractVersion=(?.*?))?\\s.*?_VERSION=(?.*)" - ] - }, { "fileMatch": ["(^|/)Dockerfile[^/]*$"], "matchStrings": [ diff --git a/data/Dockerfiles/phpfpm/Dockerfile b/data/Dockerfiles/phpfpm/Dockerfile index e9ebe071c..038c586a8 100644 --- a/data/Dockerfiles/phpfpm/Dockerfile +++ b/data/Dockerfiles/phpfpm/Dockerfile @@ -77,7 +77,7 @@ RUN apk add -U --no-cache autoconf \ --with-webp \ --with-xpm \ --with-avif \ - && docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets sysvsem zip bcmath gmp \ + && docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets zip bcmath gmp \ && docker-php-ext-configure imap --with-imap --with-imap-ssl \ && docker-php-ext-install -j 4 imap \ && curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \ diff --git a/data/assets/nextcloud/nextcloud.conf b/data/assets/nextcloud/nextcloud.conf deleted file mode 100644 index 81567d39a..000000000 --- a/data/assets/nextcloud/nextcloud.conf +++ /dev/null @@ -1,130 +0,0 @@ -map $http_x_forwarded_proto $client_req_scheme_nc { - default $scheme; - https https; -} - -server { - include /etc/nginx/conf.d/listen_ssl.active; - include /etc/nginx/conf.d/listen_plain.active; - include /etc/nginx/mime.types; - charset utf-8; - override_charset on; - - ssl_certificate /etc/ssl/mail/cert.pem; - ssl_certificate_key /etc/ssl/mail/key.pem; - 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 Referrer-Policy "no-referrer" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-Download-Options "noopen" always; - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Permitted-Cross-Domain-Policies "none" always; - add_header X-Robots-Tag "noindex, nofollow" always; - add_header X-XSS-Protection "1; mode=block" always; - - fastcgi_hide_header X-Powered-By; - - server_name NC_SUBD; - - root /web/nextcloud/; - - location = /robots.txt { - allow all; - log_not_found off; - access_log off; - } - - location = /.well-known/carddav { - return 301 $client_req_scheme_nc://$host/remote.php/dav; - } - - location = /.well-known/caldav { - return 301 $client_req_scheme_nc://$host/remote.php/dav; - } - - location = /.well-known/webfinger { - return 301 $client_req_scheme_nc://$host/index.php/.well-known/webfinger; - } - - location = /.well-known/nodeinfo { - return 301 $client_req_scheme_nc://$host/index.php/.well-known/nodeinfo; - } - - location ^~ /.well-known/acme-challenge/ { - default_type "text/plain"; - root /web; - } - - fastcgi_buffers 64 4K; - - gzip on; - gzip_vary on; - gzip_comp_level 4; - gzip_min_length 256; - gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; - gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; - set_real_ip_from fc00::/7; - 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; - real_ip_header X-Forwarded-For; - real_ip_recursive on; - - location / { - rewrite ^ /index.php$uri; - } - - location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ { - deny all; - } - location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) { - deny all; - } - - location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+)\.php(?:$|\/) { - fastcgi_split_path_info ^(.+?\.php)(\/.*|)$; - set $path_info $fastcgi_path_info; - try_files $fastcgi_script_name =404; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $path_info; - fastcgi_param HTTPS on; - # Avoid sending the security headers twice - fastcgi_param modHeadersAvailable true; - # Enable pretty urls - fastcgi_param front_controller_active true; - fastcgi_pass phpfpm:9002; - fastcgi_intercept_errors on; - fastcgi_request_buffering off; - client_max_body_size 0; - fastcgi_read_timeout 1200; - } - - location ~ ^\/(?:updater|ocs-provider)(?:$|\/) { - try_files $uri/ =404; - index index.php; - } - - location ~ \.(?:css|js|woff2?|svg|gif|map)$ { - try_files $uri /index.php$request_uri; - add_header Cache-Control "public, max-age=15778463"; - add_header Referrer-Policy "no-referrer" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-Download-Options "noopen" always; - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Permitted-Cross-Domain-Policies "none" always; - add_header X-Robots-Tag "none" always; - add_header X-XSS-Protection "1; mode=block" always; - access_log off; - } - - location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ { - try_files $uri /index.php$request_uri; - access_log off; - } -} diff --git a/data/assets/nextcloud/occ b/data/assets/nextcloud/occ deleted file mode 100755 index 5113ac01c..000000000 --- a/data/assets/nextcloud/occ +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) php /web/nextcloud/occ ${@} diff --git a/helper-scripts/nextcloud.sh b/helper-scripts/nextcloud.sh deleted file mode 100755 index 12dab3ef2..000000000 --- a/helper-scripts/nextcloud.sh +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/env bash -# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?.*)$ -NEXTCLOUD_VERSION=28.0.11 - -display_warning() { - local message=("$@") - local max_length=0 - - for line in "${message[@]}"; do - if (( ${#line} > max_length )); then - max_length=${#line} - fi - done - - local border=$(printf '%*s' "$((max_length + 4))" '' | tr ' ' '#') - - echo -e "\e[31m${border}" - for line in "${message[@]}"; do - printf "\e[31m# %-*s #\n" "$max_length" "$line" - done - echo -e "\e[31m${border}" - echo -e "\e[0m" -} - -display_warning "WARNING: This Script is deprecated and will be removed in December 2024!" \ - "mailcow will drop this installation/maintenance script within December 2024..." \ - "To ensure you can still use your Nextcloud Datas, please migrate to a standalone" \ - "Nextcloud instance either on a new Host or this host." \ - "You can either use Nextcloud in Docker or install it manually." \ - " "\ - "mailcow will NOT DELETE any Nextcloud Data, even when this script was removed!!" - -echo -e "Waiting 5 seconds before continuing..." - - -sleep 5 - -echo -ne "Checking prerequisites..." -sleep 1 -for bin in curl dirmngr tar bzip2; do - if [[ -z $(which ${bin}) ]]; then echo -ne "\r\033[31mCannot find ${bin}, exiting...\033[0m\n"; exit 1; fi -done -echo -ne "\r\033[32mFound all prerequisites! Continuing...\033[0m\n" - -[[ -z ${1} ]] && NC_HELP=y - -while [ "$1" != '' ]; do - if [[ $# -ne 1 ]]; then - echo -e "\033[31mPlease use only one parameter at the same time!\033[0m" >&2 - exit 2 - fi - case "${1}" in - -p|--purge) NC_PURGE=y && shift;; - -i|--install) NC_INSTALL=y && shift;; - -u|--update) NC_UPDATE=y && shift;; - -r|--resetpw) NC_RESETPW=y && shift;; - -h|--help) NC_HELP=y && shift;; - *) echo "Unknown parameter: ${1}" && shift;; - esac -done - -if [[ ${NC_HELP} == "y" ]]; then - printf 'Usage:\n\n' - printf ' -p|--purge\n Purge Nextcloud\n' - printf ' -i|--install\n Install Nextcloud\n' - printf ' -u|--update\n Update Nextcloud\n' - printf ' -r|--resetpw\n Reset password\n\n' - exit 0 -fi - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cd ${SCRIPT_DIR}/../ -source mailcow.conf - -if [[ ${NC_PURGE} == "y" ]]; then - read -r -p "Are you sure you want to purge Nextcloud? [y/N] " response - response=${response,,} - if [[ ! "$response" =~ ^(yes|y)$ ]]; then - echo "OK, aborting." - exit 1 - fi - - echo -e "\033[33mDetecting Database information...\033[0m" - if [[ $(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "Show databases" | grep "nextcloud") ]]; then - echo -e "\033[32mFound seperate Nextcloud database (newer scheme)!\033[0m" - echo -e "\033[31mPurging...\033[0m" - docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "DROP DATABASE nextcloud;" > /dev/null - docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "DROP USER 'nextcloud'@'%';" > /dev/null - elif [[ $(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} mailcow -e "SHOW TABLES LIKE 'oc_%'") && $? -eq 0 ]]; then - echo -e "\033[32mFound Nextcloud (oc) tables inside of mailcow database (old scheme)!\033[0m" - echo -e "\033[31mPurging...\033[0m" - docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e \ - "$(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "SELECT IFNULL(GROUP_CONCAT('DROP TABLE ', TABLE_SCHEMA, '.', TABLE_NAME SEPARATOR ';'),'SELECT NULL;') FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'oc_%' AND TABLE_SCHEMA = '${DBNAME}';" -BN)" > /dev/null - elif [[ $(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} mailcow -e "SHOW TABLES LIKE 'nc_%'") && $? -eq 0 ]]; then - echo -e "\033[32mFound Nextcloud (nc) tables inside of mailcow database (old scheme)!\033[0m" - echo -e "\033[31mPurging...\033[0m" - docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e \ - "$(docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "SELECT IFNULL(GROUP_CONCAT('DROP TABLE ', TABLE_SCHEMA, '.', TABLE_NAME SEPARATOR ';'),'SELECT NULL;') FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE 'nc_%' AND TABLE_SCHEMA = '${DBNAME}';" -BN)" > /dev/null - else - echo -e "\033[31mError: No Nextcloud databases/tables found!" - 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 <=?)(?:PHP)?(?>.+)' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then - echo -e "\033[31mError: This version of Nextcloud is not compatible with the current PHP version of php-fpm-mailcow, we'll fix it\033[0m" - wget -q https://raw.githubusercontent.com/nextcloud/server/v26.0.0/lib/versioncheck.php -O ./data/web/nextcloud/lib/versioncheck.php - echo -e "\e[33mPlease restart the update again.\e[0m" - elif ! grep -q 'installed: true' <<<$(docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings status"); then - echo -e "\033[31mError: Nextcloud seems not to be installed.\033[0m" - exit 1 - else - docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "php /web/nextcloud/updater/updater.phar" - NC_SUBD=$(docker exec -i -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:get overwritehost) - mv ./data/conf/nginx/nextcloud.conf ./data/conf/nginx/nextcloud.conf-$(date +%s).bak - cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/ - sed -i "s/NC_SUBD/${NC_SUBD}/g" ./data/conf/nginx/nextcloud.conf - fi - -elif [[ ${NC_INSTALL} == "y" ]]; then - NC_SUBD= - while [[ -z ${NC_SUBD} ]]; do - read -p "Subdomain to run Nextcloud from [format: nextcloud.domain.tld]: " NC_SUBD - done - if ! ping -q -c2 ${NC_SUBD} > /dev/null 2>&1 ; then - read -p "Cannot ping subdomain, continue anyway? [y|N] " NC_CONT_FAIL - [[ ! ${NC_CONT_FAIL,,} =~ ^(yes|y)$ ]] && { echo "Ok, exiting..."; exit 1; } - fi - - echo -e "\033[33mDownloading \033[34mNextcloud ${NEXTCLOUD_VERSION}\033[33m...\033[0m" - curl -L# -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/nextcloud-$NEXTCLOUD_VERSION.tar.bz2" || { echo "Failed to download Nextcloud archive."; exit 1; } \ - && tar -xjf nextcloud.tar.bz2 -C ./data/web/ \ - && rm nextcloud.tar.bz2 \ - && mkdir -p ./data/web/nextcloud/data \ - && chmod +x ./data/web/nextcloud/occ - - echo -e "\033[33mCreating 'nextcloud' database...\033[0m" - NC_DBPASS=$( /dev/null | head -c 28) - NC_DBUSER=nextcloud - NC_DBNAME=nextcloud - - echo -ne "[1/3] Creating 'nextcloud' database" - docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "CREATE DATABASE ${NC_DBNAME};" - sleep 2 - echo -ne "\r[2/3] Creating 'nextcloud' database user" - docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "CREATE USER '${NC_DBUSER}'@'%' IDENTIFIED BY '${NC_DBPASS}';" - sleep 2 - echo -ne "\r[3/3] Granting 'nextcloud' user all permissions on database 'nextcloud'" - docker exec -it $(docker ps -f name=mysql-mailcow -q) mysql -uroot -p${DBROOT} -e "GRANT ALL PRIVILEGES ON ${NC_DBNAME}.* TO '${NC_DBUSER}'@'%';" - sleep 2 - - echo "" - echo -e "\033[33mInstalling Nextcloud...\033[0m" - ADMIN_NC_PASS=$( /dev/null | head -c 28) - - echo -ne "[1/4] Setting correct permissions for www-data" - docker exec -it $(docker ps -f name=php-fpm-mailcow -q) /bin/bash -c "chown -R www-data:www-data /web/nextcloud" - sleep 2 - echo -ne "\r[2/4] Running occ maintenance:install to install Nextcloud" - docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ --no-warnings maintenance:install \ - --database mysql \ - --database-host mysql \ - --database-name ${NC_DBNAME} \ - --database-user ${NC_DBUSER} \ - --database-pass ${NC_DBPASS} \ - --admin-user admin \ - --admin-pass ${ADMIN_NC_PASS} \ - --data-dir /web/nextcloud/data > /dev/null 2>&1 - - echo -ne "\r[3/4] Setting custom parameters inside the Nextcloud config file" - echo "" - docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "/web/nextcloud/occ --no-warnings config:system:set redis host --value=redis --type=string; \ - /web/nextcloud/occ --no-warnings config:system:set redis port --value=6379 --type=integer; \ - /web/nextcloud/occ --no-warnings config:system:set redis timeout --value=0.0 --type=integer; \ - /web/nextcloud/occ --no-warnings config:system:set redis dbindex --value=10 --type=integer; \ - /web/nextcloud/occ --no-warnings config:system:set memcache.locking --value='\OC\Memcache\Redis' --type=string; \ - /web/nextcloud/occ --no-warnings config:system:set memcache.local --value='\OC\Memcache\Redis' --type=string; \ - /web/nextcloud/occ --no-warnings config:system:set trusted_domains 1 --value=${NC_SUBD}; \ - /web/nextcloud/occ --no-warnings config:system:set trusted_proxies 0 --value=${IPV6_NETWORK}; \ - /web/nextcloud/occ --no-warnings config:system:set trusted_proxies 1 --value=${IPV4_NETWORK}.0/24; \ - /web/nextcloud/occ --no-warnings config:system:set overwritehost --value=${NC_SUBD}; \ - /web/nextcloud/occ --no-warnings config:system:set overwriteprotocol --value=https; \ - /web/nextcloud/occ --no-warnings config:system:set overwritewebroot --value=/; \ - /web/nextcloud/occ --no-warnings config:system:set mail_smtpmode --value=smtp; \ - /web/nextcloud/occ --no-warnings config:system:set mail_smtpauthtype --value=LOGIN; \ - /web/nextcloud/occ --no-warnings config:system:set mail_from_address --value=nextcloud; \ - /web/nextcloud/occ --no-warnings config:system:set mail_domain --value=${MAILCOW_HOSTNAME}; \ - /web/nextcloud/occ --no-warnings config:system:set mail_smtphost --value=postfix; \ - /web/nextcloud/occ --no-warnings config:system:set mail_smtpport --value=588; \ - /web/nextcloud/occ --no-warnings config:system:set mail_smtpstreamoptions ssl verify_peer --value=false --type=boolean - /web/nextcloud/occ --no-warnings config:system:set mail_smtpstreamoptions ssl verify_peer_name --value=false --type=boolean - /web/nextcloud/occ --no-warnings db:convert-filecache-bigint -n" - - # Not installing by default, broke too often - #/web/nextcloud/occ --no-warnings app:install user_external; \ - #/web/nextcloud/occ --no-warnings config:system:set user_backends 0 arguments 0 --value={dovecot:143/imap/tls/novalidate-cert}; \ - #/web/nextcloud/occ --no-warnings config:system:set user_backends 0 class --value=OC_User_IMAP; \ - - echo -e "\r[4/4] Enabling Nginx Configuration" - cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/ - sed -i "s/NC_SUBD/${NC_SUBD}/g" ./data/conf/nginx/nextcloud.conf - sleep 2 - - echo "" - echo -e "\033[33mFinalizing installation...\033[0m" - docker restart $(docker ps -aqf name=nginx-mailcow) - - echo "" - echo "******************************************" - echo "* SAVE THESE CREDENTIALS *" - echo "* INSTALL DATE: $(date +%Y-%m-%d_%H-%M-%S) *" - echo "******************************************" - echo "" - echo -e "\033[36mDatabase name: ${NC_DBNAME}\033[0m" - echo -e "\033[36mDatabase user: ${NC_DBUSER}\033[0m" - echo -e "\033[36mDatabase password: ${NC_DBPASS}\033[0m" - echo "" - echo -e "\033[31mUI admin password: ${ADMIN_NC_PASS}\033[0m" - echo "" - - -elif [[ ${NC_RESETPW} == "y" ]]; then - printf 'You are about to set a new password for a Nextcloud user.\n\nDo not use this option if your Nextcloud is configured to use mailcow for authentication.\nSet a new password for the corresponding mailbox in mailcow, instead.\n\n' - read -r -p "Continue? [y/N] " response - response=${response,,} - if [[ ! "$response" =~ ^(yes|y)$ ]]; then - echo "OK, aborting." - exit 1 - fi - - NC_USER= - while [[ -z ${NC_USER} ]]; do - read -p "Enter the username: " NC_USER - done - docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ user:resetpassword ${NC_USER} -fi From 60ca25026dd194ae8f2681df761070fa9b1ce414 Mon Sep 17 00:00:00 2001 From: Filip Marek Date: Fri, 9 Aug 2024 17:23:48 +0200 Subject: [PATCH 23/52] add temporary email description --- data/web/inc/functions.mailbox.inc.php | 7 ++++-- data/web/inc/init_db.inc.php | 1 + data/web/js/site/user.js | 5 ++++ data/web/lang/lang.ca-es.json | 3 ++- data/web/lang/lang.cs-cz.json | 3 ++- data/web/lang/lang.da-dk.json | 3 ++- data/web/lang/lang.de-de.json | 3 ++- data/web/lang/lang.en-gb.json | 3 ++- data/web/lang/lang.es-es.json | 3 ++- data/web/lang/lang.fi-fi.json | 3 ++- data/web/lang/lang.fr-fr.json | 3 ++- data/web/lang/lang.hu-hu.json | 3 ++- data/web/lang/lang.it-it.json | 3 ++- data/web/lang/lang.ko-kr.json | 3 ++- data/web/lang/lang.lv-lv.json | 3 ++- data/web/lang/lang.nl-nl.json | 3 ++- data/web/lang/lang.pl-pl.json | 3 ++- data/web/lang/lang.pt-br.json | 3 ++- data/web/lang/lang.pt-pt.json | 3 ++- data/web/lang/lang.ro-ro.json | 3 ++- data/web/lang/lang.ru-ru.json | 3 ++- data/web/lang/lang.sk-sk.json | 3 ++- data/web/lang/lang.sv-se.json | 3 ++- data/web/lang/lang.tr-tr.json | 3 ++- data/web/lang/lang.uk-ua.json | 3 ++- data/web/lang/lang.zh-cn.json | 3 ++- data/web/lang/lang.zh-tw.json | 3 ++- data/web/templates/modals/user.twig | 29 ++++++++---------------- data/web/templates/user/SpamAliases.twig | 2 +- 29 files changed, 70 insertions(+), 46 deletions(-) diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 73c115411..069b40c29 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -48,6 +48,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $_data["validity"] = 8760; } $domain = $_data['domain']; + $description = $_data['description']; $valid_domains[] = mailbox('get', 'mailbox_details', $username)['domain']; $valid_alias_domains = user_get_alias_details($username)['alias_domains']; if (!empty($valid_alias_domains)) { @@ -62,10 +63,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { return false; } $validity = strtotime("+" . $_data["validity"] . " hour"); - $stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `goto`, `validity`) VALUES - (:address, :goto, :validity)"); + $stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `description`, `goto`, `validity`) VALUES + (:address, :description, :goto, :validity)"); $stmt->execute(array( ':address' => readable_random_string(rand(rand(3, 9), rand(3, 9))) . '.' . readable_random_string(rand(rand(3, 9), rand(3, 9))) . '@' . $domain, + ':description' => $description, ':goto' => $username, ':validity' => $validity )); @@ -4201,6 +4203,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { } $stmt = $pdo->prepare("SELECT `address`, `goto`, + `description`, `validity`, `created`, `modified` diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php index 3cfeb37df..67a6dff8b 100644 --- a/data/web/inc/init_db.inc.php +++ b/data/web/inc/init_db.inc.php @@ -527,6 +527,7 @@ function init_db_schema() { "cols" => array( "address" => "VARCHAR(255) NOT NULL", "goto" => "TEXT NOT NULL", + "description" => "TEXT NOT NULL", "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", "modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP", "validity" => "INT(11)" diff --git a/data/web/js/site/user.js b/data/web/js/site/user.js index 59d65d8ce..c1a680805 100644 --- a/data/web/js/site/user.js +++ b/data/web/js/site/user.js @@ -202,6 +202,11 @@ jQuery(function($){ data: 'address', defaultContent: '' }, + { + title: lang.description, + data: 'description', + defaultContent: '' + }, { title: lang.alias_valid_until, data: 'validity', diff --git a/data/web/lang/lang.ca-es.json b/data/web/lang/lang.ca-es.json index 877b46cdb..43079339d 100644 --- a/data/web/lang/lang.ca-es.json +++ b/data/web/lang/lang.ca-es.json @@ -493,6 +493,7 @@ "client_configuration": "Guies de configuració per als clients de correu més habituals", "create_syncjob": "Afegir treball de sincronitzaió", "day": "Dia", + "description": "Descripció", "direct_aliases": "Adreces àlies directes", "direct_aliases_desc": "Els àlies directes sí que es veuen afectat per la configuració de l'usuari", "eas_reset": "Fer un reset de la cache d'ActiveSync del dispositiu", @@ -558,4 +559,4 @@ "week": "Setmana", "weeks": "Setmanes" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.cs-cz.json b/data/web/lang/lang.cs-cz.json index e72fb216a..6efdd31e4 100644 --- a/data/web/lang/lang.cs-cz.json +++ b/data/web/lang/lang.cs-cz.json @@ -1164,6 +1164,7 @@ "created_on": "Vytvoreno", "daily": "Každý den", "day": "den", + "description": "Popis", "delete_ays": "Potvrďte odstranění.", "direct_aliases": "Přímé aliasy", "direct_aliases_desc": "Na přímé aliasy se uplatňuje filtr spamu a nastavení pravidel TLS", @@ -1309,4 +1310,4 @@ "session_token": "Token formuláře není platný: Token mismatch", "session_ua": "Token formuláře není platný: User-Agent validation error" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.da-dk.json b/data/web/lang/lang.da-dk.json index 95db916ef..f0f3bb922 100644 --- a/data/web/lang/lang.da-dk.json +++ b/data/web/lang/lang.da-dk.json @@ -971,6 +971,7 @@ "create_syncjob": "Opret nyt sync job", "daily": "Dagligt", "day": "dag", + "description": "Beskrivelse", "delete_ays": "Bekræft venligst ønsket om sletning.", "direct_aliases": "Direkte alias addresser", "direct_aliases_desc": "Direkte alias-adresser påvirkes af spamfilter og TLS-politiske indstillinger.", @@ -1091,4 +1092,4 @@ "first": "Først" } } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.de-de.json b/data/web/lang/lang.de-de.json index 807393d77..d8eb5de79 100644 --- a/data/web/lang/lang.de-de.json +++ b/data/web/lang/lang.de-de.json @@ -1185,6 +1185,7 @@ "created_on": "Erstellt am", "daily": "Täglich", "day": "Tag", + "description": "Beschreibung", "delete_ays": "Soll der Löschvorgang wirklich ausgeführt werden?", "direct_aliases": "Direkte Alias-Adressen", "direct_aliases_desc": "Nur direkte Alias-Adressen werden für benutzerdefinierte Einstellungen berücksichtigt.", @@ -1338,4 +1339,4 @@ "hour": "Nachrichten / Stunde", "day": "Nachrichten / Tag" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.en-gb.json b/data/web/lang/lang.en-gb.json index 6e898099b..28d9ca148 100644 --- a/data/web/lang/lang.en-gb.json +++ b/data/web/lang/lang.en-gb.json @@ -1192,6 +1192,7 @@ "created_on": "Created on", "daily": "Daily", "day": "day", + "description": "Description", "delete_ays": "Please confirm the deletion process.", "direct_aliases": "Direct alias addresses", "direct_aliases_desc": "Direct alias addresses are affected by spam filter and TLS policy settings.", @@ -1338,4 +1339,4 @@ "session_token": "Form token invalid: Token mismatch", "session_ua": "Form token invalid: User-Agent validation error" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.es-es.json b/data/web/lang/lang.es-es.json index 8a8c05274..6cc02f5b6 100644 --- a/data/web/lang/lang.es-es.json +++ b/data/web/lang/lang.es-es.json @@ -709,6 +709,7 @@ "create_syncjob": "Crear nuevo trabajo de sincronización", "daily": "Cada día", "day": "Día", + "description": "Descripción", "direct_aliases": "Alias directos", "direct_aliases_desc": "Los alias directos se ven afectadas por el filtro de correo no deseado y la configuración de la política TLS del usuario.", "eas_reset": "Resetear el caché ActiveSync", @@ -778,4 +779,4 @@ "fuzzy_learn_error": "Error aprendiendo hash: %s", "ip_invalid": "IP inválida omitida: %s" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.fi-fi.json b/data/web/lang/lang.fi-fi.json index 9b3cc8488..dfa8e8167 100644 --- a/data/web/lang/lang.fi-fi.json +++ b/data/web/lang/lang.fi-fi.json @@ -810,6 +810,7 @@ "create_syncjob": "Luo uusi synkronointi työ", "daily": "Päivittäin", "day": "Päivä", + "description": "Kuvaus", "direct_aliases": "Suorat alias osoitteet", "direct_aliases_desc": "Roska posti suodatus-ja TLS-käytäntö asetukset vaikuttavat suora aliaksen osoitteisiin.", "eas_reset": "Tyhjennä ActiveSync-laitteen väli muisti", @@ -908,4 +909,4 @@ "last": "Edellinen" } } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.fr-fr.json b/data/web/lang/lang.fr-fr.json index 386a4dfc6..56c888147 100644 --- a/data/web/lang/lang.fr-fr.json +++ b/data/web/lang/lang.fr-fr.json @@ -1101,6 +1101,7 @@ "create_syncjob": "Créer une tâche de synchronisation", "daily": "Quotidien", "day": "jour", + "description": "Description", "delete_ays": "Veuillez confirmer le processus de suppression.", "direct_aliases": "Adresses alias directes", "direct_aliases_desc": "Les adresses d’alias directes sont affectées par le filtre anti-spam et les paramètres de politique TLS.", @@ -1267,4 +1268,4 @@ "ratelimit": { "disabled": "Désactivé" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.hu-hu.json b/data/web/lang/lang.hu-hu.json index e9762ff51..5737794fd 100644 --- a/data/web/lang/lang.hu-hu.json +++ b/data/web/lang/lang.hu-hu.json @@ -406,6 +406,7 @@ "create_syncjob": "Új szinkronizációs művelet létrehozása", "daily": "Napi", "day": "nap", + "description": "Leírás", "delete_ays": "Erősítse meg a törlést.", "direct_aliases": "Közvetlen alias címek", "eas_reset": "ActiveSync eszköz gyorsítótár ürítése", @@ -591,4 +592,4 @@ "app_name": "Alkalmazás neve", "app_passwd_protocols": "Engedélyezett protokollok az alkalmazás jelszavához" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.it-it.json b/data/web/lang/lang.it-it.json index f73f938bf..d0d28fce8 100644 --- a/data/web/lang/lang.it-it.json +++ b/data/web/lang/lang.it-it.json @@ -1126,6 +1126,7 @@ "created_on": "Creato il", "daily": "Giornaliero", "day": "giorno", + "description": "Descrizione", "delete_ays": "Please confirm the deletion process.", "direct_aliases": "Direct alias addresses", "direct_aliases_desc": "Direct alias addresses are affected by spam filter and TLS policy settings.", @@ -1308,4 +1309,4 @@ }, "decimal": "." } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.ko-kr.json b/data/web/lang/lang.ko-kr.json index 3f3cb1535..14c7966d0 100644 --- a/data/web/lang/lang.ko-kr.json +++ b/data/web/lang/lang.ko-kr.json @@ -911,6 +911,7 @@ "create_syncjob": "새 동기화 작업 생성", "daily": "매일", "day": "일", + "description": "Description", "delete_ays": "진짜 삭제하겠습니까?", "direct_aliases": "Direct alias addresses", "direct_aliases_desc": "Direct alias addresses are affected by spam filter and TLS policy settings.", @@ -1020,4 +1021,4 @@ "session_token": "Form token invalid: Token mismatch", "session_ua": "Form token invalid: User-Agent validation error" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.lv-lv.json b/data/web/lang/lang.lv-lv.json index 98cb04ae8..856566ae4 100644 --- a/data/web/lang/lang.lv-lv.json +++ b/data/web/lang/lang.lv-lv.json @@ -551,6 +551,7 @@ "client_configuration": "Parādīt konfigurācijas norādes e-pasta klientiem un tālruņiem", "create_syncjob": "Izveidot jaunu sinhronizācijas darbu", "day": "Dienas", + "description": "Apraksts", "direct_aliases": "Tiešas aizstājadreses", "direct_aliases_desc": "Tiešās aizstājadreses ir surogātpasta atlasīšanas un TLS nosacījumu iestatījumu ietekmētas.", "eas_reset": "Atiestatīt ActiveSync ierīces kešatmiņu", @@ -659,4 +660,4 @@ "fido2": { "fido2_auth": "Pieteikties ar FIDO2" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.nl-nl.json b/data/web/lang/lang.nl-nl.json index fdfc91c70..49113cd31 100644 --- a/data/web/lang/lang.nl-nl.json +++ b/data/web/lang/lang.nl-nl.json @@ -1028,6 +1028,7 @@ "create_syncjob": "Voeg sync job toe", "daily": "Dagelijks", "day": "dag", + "description": "Beschrijving", "delete_ays": "Bevestig de verwijdering.", "direct_aliases": "Directe aliasadressen", "direct_aliases_desc": "Directe aliasadressen worden beïnvloed door spamfilters en het versleutelingsbeleid.", @@ -1167,4 +1168,4 @@ "search": "Zoeken:", "zeroRecords": "Geen overeenkomsten gevonden" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.pl-pl.json b/data/web/lang/lang.pl-pl.json index aa185d32e..cba7372a0 100644 --- a/data/web/lang/lang.pl-pl.json +++ b/data/web/lang/lang.pl-pl.json @@ -363,6 +363,7 @@ "create_syncjob": "Utwórz nowe polecenie synchronizacji", "daily": "Co dzień", "day": "Dzień", + "description": "Opis", "direct_aliases": "Aliasy bezpośrednie", "direct_aliases_desc": "Na aliasy bezpośrednie wpływają filtry spamu i ustawienia TLS.", "eas_reset": "Zresetuj pamięć podręczną urządzenia ActiveSync", @@ -431,4 +432,4 @@ "weekly": "Co tydzień", "weeks": "Tygodnie" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.pt-br.json b/data/web/lang/lang.pt-br.json index 2fbd43535..7afea2587 100644 --- a/data/web/lang/lang.pt-br.json +++ b/data/web/lang/lang.pt-br.json @@ -1186,6 +1186,7 @@ "created_on": "Criado em", "daily": "Diariamente", "day": "dia", + "description": "Descrição", "delete_ays": "Confirme o processo de exclusão.", "direct_aliases": "Endereços de alias diretos", "direct_aliases_desc": "Os endereços de alias diretos são afetados pelo filtro de spam e pelas configurações da política TLS.", @@ -1332,4 +1333,4 @@ "session_token": "Token de formulário inválido: incompatibilidade de token", "session_ua": "Token de formulário inválido: erro de validação do agente de usuário" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.pt-pt.json b/data/web/lang/lang.pt-pt.json index 624acd6db..2279c2cec 100644 --- a/data/web/lang/lang.pt-pt.json +++ b/data/web/lang/lang.pt-pt.json @@ -236,6 +236,7 @@ "aliases_send_as_all": "Não verificar remetente para os domínios", "change_password": "Alterar senha", "day": "Dia", + "description": "Descrição", "edit": "Editar", "hour": "Hora", "hours": "Horas", @@ -272,4 +273,4 @@ "week": "Semana", "weeks": "Semanas" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.ro-ro.json b/data/web/lang/lang.ro-ro.json index 90c96c218..04fb31fed 100644 --- a/data/web/lang/lang.ro-ro.json +++ b/data/web/lang/lang.ro-ro.json @@ -1064,6 +1064,7 @@ "created_on": "Creat în", "daily": "Zilnic", "day": "zi", + "description": "Descriere", "delete_ays": "Vă rugăm să confirmați stergerea.", "direct_aliases": "Adrese alias directe", "direct_aliases_desc": "Adresele alias directe sunt afectate de setările filtrului de spam și ale politicii TLS.", @@ -1210,4 +1211,4 @@ "expand_all": "Expandează tot", "decimal": "," } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.ru-ru.json b/data/web/lang/lang.ru-ru.json index 5d1ef9af2..b021fd695 100644 --- a/data/web/lang/lang.ru-ru.json +++ b/data/web/lang/lang.ru-ru.json @@ -1192,6 +1192,7 @@ "created_on": "Дата создания", "daily": "Раз в день", "day": "день", + "description": "Описание", "delete_ays": "Пожалуйста, подтвердите удаление", "direct_aliases": "Личные псевдонимы", "direct_aliases_desc": "На личные псевдонимы распространяются фильтры нежелательной почты и параметры политики TLS.", @@ -1338,4 +1339,4 @@ "session_token": "Неверный токен формы: несоответствие токена", "session_ua": "Неверный токен формы: ошибка проверки User-Agent" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.sk-sk.json b/data/web/lang/lang.sk-sk.json index 466afdb85..0abe95d4f 100644 --- a/data/web/lang/lang.sk-sk.json +++ b/data/web/lang/lang.sk-sk.json @@ -1116,6 +1116,7 @@ "created_on": "Vytvorené", "daily": "Denne", "day": "deň", + "description": "Popis", "delete_ays": "Potvrďte zmazanie.", "direct_aliases": "Priame alias adresy", "direct_aliases_desc": "Priame aliasy sú ovplyvnené spam filtrom a nastavením TLS pravidiel.", @@ -1258,4 +1259,4 @@ "session_token": "Formulárový token neplatný: Tokenová nezhoda", "session_ua": "Formulárový token neplatný: User-Agent validation error" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.sv-se.json b/data/web/lang/lang.sv-se.json index bbf0d9586..31cc14874 100644 --- a/data/web/lang/lang.sv-se.json +++ b/data/web/lang/lang.sv-se.json @@ -993,6 +993,7 @@ "created_on": "Skapad vid", "daily": "Dagligen", "day": "dag", + "description": "Beskrivning", "delete_ays": "Är du säker att du vill ta bort det här objektet?", "direct_aliases": "Direkta aliasadresser", "direct_aliases_desc": "Endast direkta aliasadresser påverkas av spamfilter och TLS-policyföreskrifter.", @@ -1112,4 +1113,4 @@ "session_token": "Formulär-nyckeln är ogiltig: Nyckeln matchar inte", "session_ua": "Formulär-nyckeln är ogiltig: User-Agenten kunde inte valideras" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.tr-tr.json b/data/web/lang/lang.tr-tr.json index 7049046c2..4d18a73ba 100644 --- a/data/web/lang/lang.tr-tr.json +++ b/data/web/lang/lang.tr-tr.json @@ -1207,6 +1207,7 @@ "created_on": "Oluşturulma tarihi", "daily": "Günlük", "day": "Gün", + "description": "Açıklama", "delete_ays": "Lütfen silme işlemini onaylayın.", "direct_aliases": "Doğrudan takma ad adresleri", "eas_reset_help": "Birçok durumda cihaz önbelleğini sıfırlama, bozuk bir ActiveSync profilini kurtarmaya yardımcı olur.
Dikkat: Tüm öğeler yeniden indirilecek!", @@ -1313,4 +1314,4 @@ "q_reject": "Reddedildi", "week": "Hafta" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.uk-ua.json b/data/web/lang/lang.uk-ua.json index f2884cd47..d003b9fda 100644 --- a/data/web/lang/lang.uk-ua.json +++ b/data/web/lang/lang.uk-ua.json @@ -1135,6 +1135,7 @@ "created_on": "Дата створення", "daily": "Раз на день", "day": "день", + "description": "Опис", "delete_ays": "Будь ласка, підтвердіть видалення.", "eas_reset": "Скинути кеш ActiveSync пристроїв", "eas_reset_help": "У багатьох випадках скидання кешу пристроїв допомагає відновити пошкоджений профіль ActiveSync.
Увага: всі листи, календарі та контакти будуть завантажені заново на всі ваші пристрої!", @@ -1311,4 +1312,4 @@ }, "collapse_all": "Згорнути все" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.zh-cn.json b/data/web/lang/lang.zh-cn.json index ca6c9aaf2..5cdaef09e 100644 --- a/data/web/lang/lang.zh-cn.json +++ b/data/web/lang/lang.zh-cn.json @@ -1164,6 +1164,7 @@ "created_on": "添加于", "daily": "每日", "day": "日", + "description": "描述", "delete_ays": "请确认删除。", "direct_aliases": "直接别名", "direct_aliases_desc": "垃圾邮件过滤和 TLS 策略会作用于直接别名。", @@ -1336,4 +1337,4 @@ "loadingRecords": "加载中...", "zeroRecords": "未找到符合条件的记录" } -} +} \ No newline at end of file diff --git a/data/web/lang/lang.zh-tw.json b/data/web/lang/lang.zh-tw.json index 2017d1ddd..e3cc3aed9 100644 --- a/data/web/lang/lang.zh-tw.json +++ b/data/web/lang/lang.zh-tw.json @@ -1140,6 +1140,7 @@ "created_on": "建立於", "daily": "每日", "day": "日", + "description": "描述", "delete_ays": "請確認刪除。", "direct_aliases": "直接別名", "direct_aliases_desc": "直接別名會受到垃圾郵件過濾器和 TLS 規則限制。", @@ -1328,4 +1329,4 @@ "hold_mail": "保留", "unhold_mail": "取消保留" } -} +} \ No newline at end of file diff --git a/data/web/templates/modals/user.twig b/data/web/templates/modals/user.twig index c9cd4b97e..b8e3bf3ab 100644 --- a/data/web/templates/modals/user.twig +++ b/data/web/templates/modals/user.twig @@ -340,31 +340,22 @@