Merge pull request #5162 from mailcow/staging

Update 2023-04
This commit is contained in:
Niklas Meyer 2023-04-03 14:55:55 +02:00 committed by GitHub
commit 028ef22878
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 189 additions and 78 deletions

View File

@ -14,7 +14,7 @@ jobs:
pull-requests: write
steps:
- name: Mark/Close Stale Issues and Pull Requests 🗑️
uses: actions/stale@v7.0.0
uses: actions/stale@v8.0.0
with:
repo-token: ${{ secrets.STALE_ACTION_PAT }}
days-before-stale: 60

View File

@ -380,7 +380,12 @@ class DockerUtils:
if 'maildir' in request_json:
for container in self.docker_client.containers.list(filters={"id": container_id}):
sane_name = re.sub(r'\W+', '', request_json['maildir'])
cmd = ["/bin/bash", "-c", "if [[ -d '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' ]]; then /bin/mv '/var/vmail/" + request_json['maildir'].replace("'", "'\\''") + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"]
vmail_name = request_json['maildir'].replace("'", "'\\''")
index_name = request_json['maildir'].split("/")
index_name = index_name[1].replace("'", "'\\''") + "@" + index_name[0].replace("'", "'\\''")
cmd_vmail = "if [[ -d '/var/vmail/" + vmail_name + "' ]]; then /bin/mv '/var/vmail/" + vmail_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "'; fi"
cmd_vmail_index = "if [[ -d '/var/vmail_index/" + index_name + "' ]]; then /bin/mv '/var/vmail_index/" + index_name + "' '/var/vmail/_garbage/" + str(int(time.time())) + "_" + sane_name + "_index'; fi"
cmd = ["/bin/bash", "-c", cmd_vmail + " && " + cmd_vmail_index]
maildir_cleanup = container.exec_run(cmd, user='vmail')
return exec_run_handler('generic', maildir_cleanup)
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password

View File

@ -64,28 +64,40 @@ def refreshF2boptions():
global f2boptions
global quit_now
global exit_code
if not r.get('F2B_OPTIONS'):
f2boptions = {}
f2boptions['ban_time'] = int
f2boptions['max_attempts'] = int
f2boptions['retry_window'] = int
f2boptions['netban_ipv4'] = int
f2boptions['netban_ipv6'] = int
f2boptions['ban_time'] = r.get('F2B_BAN_TIME') or 1800
f2boptions['max_attempts'] = r.get('F2B_MAX_ATTEMPTS') or 10
f2boptions['retry_window'] = r.get('F2B_RETRY_WINDOW') or 600
f2boptions['netban_ipv4'] = r.get('F2B_NETBAN_IPV4') or 32
f2boptions['netban_ipv6'] = r.get('F2B_NETBAN_IPV6') or 128
r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
if not r.get('F2B_OPTIONS'):
f2boptions['ban_time'] = r.get('F2B_BAN_TIME')
f2boptions['max_ban_time'] = r.get('F2B_MAX_BAN_TIME')
f2boptions['ban_time_increment'] = r.get('F2B_BAN_TIME_INCREMENT')
f2boptions['max_attempts'] = r.get('F2B_MAX_ATTEMPTS')
f2boptions['retry_window'] = r.get('F2B_RETRY_WINDOW')
f2boptions['netban_ipv4'] = r.get('F2B_NETBAN_IPV4')
f2boptions['netban_ipv6'] = r.get('F2B_NETBAN_IPV6')
else:
try:
f2boptions = {}
f2boptions = json.loads(r.get('F2B_OPTIONS'))
except ValueError:
print('Error loading F2B options: F2B_OPTIONS is not json')
quit_now = True
exit_code = 2
verifyF2boptions(f2boptions)
r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
def verifyF2boptions(f2boptions):
verifyF2boption(f2boptions,'ban_time', 1800)
verifyF2boption(f2boptions,'max_ban_time', 10000)
verifyF2boption(f2boptions,'ban_time_increment', True)
verifyF2boption(f2boptions,'max_attempts', 10)
verifyF2boption(f2boptions,'retry_window', 600)
verifyF2boption(f2boptions,'netban_ipv4', 32)
verifyF2boption(f2boptions,'netban_ipv6', 128)
def verifyF2boption(f2boptions, f2boption, f2bdefault):
f2boptions[f2boption] = f2boptions[f2boption] if f2boption in f2boptions and f2boptions[f2boption] is not None else f2bdefault
def refreshF2bregex():
global f2bregex
global quit_now
@ -147,6 +159,7 @@ def ban(address):
global lock
refreshF2boptions()
BAN_TIME = int(f2boptions['ban_time'])
BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment'])
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
RETRY_WINDOW = int(f2boptions['retry_window'])
NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
@ -174,20 +187,16 @@ def ban(address):
net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False)
net = str(net)
if not net in bans or time.time() - bans[net]['last_attempt'] > RETRY_WINDOW:
bans[net] = { 'attempts': 0 }
active_window = RETRY_WINDOW
else:
active_window = time.time() - bans[net]['last_attempt']
if not net in bans:
bans[net] = {'attempts': 0, 'last_attempt': 0, 'ban_counter': 0}
bans[net]['attempts'] += 1
bans[net]['last_attempt'] = time.time()
active_window = time.time() - bans[net]['last_attempt']
if bans[net]['attempts'] >= MAX_ATTEMPTS:
cur_time = int(round(time.time()))
logCrit('Banning %s for %d minutes' % (net, BAN_TIME / 60))
NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter']
logCrit('Banning %s for %d minutes' % (net, NET_BAN_TIME / 60 ))
if type(ip) is ipaddress.IPv4Address:
with lock:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
@ -206,7 +215,7 @@ def ban(address):
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + BAN_TIME)
r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + NET_BAN_TIME)
else:
logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
@ -238,7 +247,8 @@ def unban(net):
r.hdel('F2B_ACTIVE_BANS', '%s' % net)
r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
if net in bans:
del bans[net]
bans[net]['attempts'] = 0
bans[net]['ban_counter'] += 1
def permBan(net, unban=False):
global lock
@ -332,7 +342,7 @@ def watch():
logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data']))
ban(addr)
except Exception as ex:
logWarn('Error reading log line from pubsub')
logWarn('Error reading log line from pubsub: %s' % ex)
quit_now = True
exit_code = 2
@ -366,6 +376,8 @@ def snat4(snat_target):
chain.insert_rule(new_rule)
else:
for position, rule in enumerate(chain.rules):
if not hasattr(rule.target, 'parameter'):
continue
match = all((
new_rule.get_src() == rule.get_src(),
new_rule.get_dst() == rule.get_dst(),
@ -425,6 +437,8 @@ def autopurge():
time.sleep(10)
refreshF2boptions()
BAN_TIME = int(f2boptions['ban_time'])
MAX_BAN_TIME = int(f2boptions['max_ban_time'])
BAN_TIME_INCREMENT = bool(f2boptions['ban_time_increment'])
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
if QUEUE_UNBAN:
@ -432,7 +446,9 @@ def autopurge():
unban(str(net))
for net in bans.copy():
if bans[net]['attempts'] >= MAX_ATTEMPTS:
if time.time() - bans[net]['last_attempt'] > BAN_TIME:
NET_BAN_TIME = BAN_TIME if not BAN_TIME_INCREMENT else BAN_TIME * 2 ** bans[net]['ban_counter']
TIME_SINCE_LAST_ATTEMPT = time.time() - bans[net]['last_attempt']
if TIME_SINCE_LAST_ATTEMPT > NET_BAN_TIME or TIME_SINCE_LAST_ATTEMPT > MAX_BAN_TIME:
unban(net)
def isIpNetwork(address):

View File

@ -1,4 +1,4 @@
FROM php:8.1-fpm-alpine3.17
FROM php:8.2-fpm-alpine3.17
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced
@ -12,7 +12,7 @@ ARG MEMCACHED_PECL_VERSION=3.2.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced
ARG REDIS_PECL_VERSION=5.3.7
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced
ARG COMPOSER_VERSION=2.5.4
ARG COMPOSER_VERSION=2.5.5
RUN apk add -U --no-cache autoconf \
aspell-dev \
@ -52,6 +52,7 @@ RUN apk add -U --no-cache autoconf \
libxpm-dev \
libzip \
libzip-dev \
linux-headers \
make \
mysql-client \
openldap-dev \
@ -75,7 +76,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 zip bcmath gmp \
&& 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-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} \
@ -99,6 +100,7 @@ RUN apk add -U --no-cache autoconf \
libxml2-dev \
libxpm-dev \
libzip-dev \
linux-headers \
make \
openldap-dev \
pcre-dev \

View File

@ -24,7 +24,7 @@ server {
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-Robots-Tag "noindex, nofollow" always;
add_header X-XSS-Protection "1; mode=block" always;
fastcgi_hide_header X-Powered-By;

View File

@ -8,7 +8,7 @@ VIRUS_FOUND {
}
# Bad policy from free mail providers
FREEMAIL_POLICY_FAILURE {
expression = "-g+:policies & !DMARC_POLICY_ALLOW & !MAILLIST & ( FREEMAIL_ENVFROM | FREEMAIL_FROM ) & !WHITELISTED_FWD_HOST";
expression = "FREEMAIL_FROM & !DMARC_POLICY_ALLOW & !MAILLIST& !WHITELISTED_FWD_HOST & -g+:policies";
score = 16.0;
}
# Applies to freemail with undisclosed recipients

View File

@ -62,7 +62,7 @@
SOGoFirstDayOfWeek = "1";
SOGoSieveFolderEncoding = "UTF-8";
SOGoPasswordChangeEnabled = YES;
SOGoPasswordChangeEnabled = NO;
SOGoSentFolderName = "Sent";
SOGoMailShowSubscribedFoldersOnly = NO;
NGImap4ConnectionStringSeparator = "/";

View File

@ -3176,8 +3176,10 @@ paths:
example:
attr:
ban_time: "86400"
ban_time_increment: "1"
blacklist: "10.100.6.5/32,10.100.8.4/32"
max_attempts: "5"
max_ban_time: "86400"
netban_ipv4: "24"
netban_ipv6: "64"
retry_window: "600"
@ -3191,11 +3193,17 @@ paths:
description: the backlisted ips or hostnames separated by comma
type: string
ban_time:
description: the time a ip should be banned
description: the time an ip should be banned
type: number
ban_time_increment:
description: if the time of the ban should increase each time
type: boolean
max_attempts:
description: the maximum numbe of wrong logins before a ip is banned
type: number
max_ban_time:
description: the maximum time an ip should be banned
type: number
netban_ipv4:
description: the networks mask to ban for ipv4
type: number
@ -4113,10 +4121,12 @@ paths:
response:
value:
ban_time: 604800
ban_time_increment: 1
blacklist: |-
45.82.153.37/32
92.118.38.52/32
max_attempts: 1
max_ban_time: 604800
netban_ipv4: 32
netban_ipv6: 128
perm_bans:

View File

@ -239,7 +239,9 @@ function fail2ban($_action, $_data = null) {
$is_now = fail2ban('get');
if (!empty($is_now)) {
$ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
$ban_time_increment = (isset($_data['ban_time_increment']) && $_data['ban_time_increment'] == "1") ? 1 : 0;
$max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['max_attempts']);
$max_ban_time = intval((isset($_data['max_ban_time'])) ? $_data['max_ban_time'] : $is_now['max_ban_time']);
$retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']);
$netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']);
$netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']);
@ -256,6 +258,8 @@ function fail2ban($_action, $_data = null) {
}
$f2b_options = array();
$f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
$f2b_options['ban_time_increment'] = ($ban_time_increment == 1) ? true : false;
$f2b_options['max_ban_time'] = ($max_ban_time < 60) ? 60 : $max_ban_time;
$f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
$f2b_options['netban_ipv6'] = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
$f2b_options['netban_ipv4'] = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;

View File

@ -1181,7 +1181,7 @@ jQuery(function($){
if (table = $('#' + log_table).DataTable()) {
var heading = $('#' + log_table).closest('.card').find('.card-header');
var load_rows = (table.page.len() + 1) + '-' + (table.page.len() + new_nrows)
var load_rows = (table.data().length + 1) + '-' + (table.data().length + new_nrows)
$.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }

View File

@ -83,7 +83,7 @@
"private_comment": "Privat kommentar",
"public_comment": "Offentlig kommentar",
"quota_mb": "Kvota (Mb)",
"relay_all": "Send alle modtagere videre",
"relay_all": "Besvar alle modtager",
"relay_all_info": "↪ Hvis du vælger <b> ikke </b> at videresende alle modtagere, skal du tilføje et (\"blind\") postkasse til hver enkelt modtager, der skal videresendes.",
"relay_domain": "Send dette domæne videre",
"relay_transport_info": "<div class=\"badge fs-6 bg-info\">Info</div> Du kan definere transportkort til en tilpasset destination for dette domæne. Hvis ikke indstillet, foretages der et MX-opslag.",
@ -104,7 +104,10 @@
"timeout2": "Timeout for forbindelse til lokal vært",
"username": "Brugernavn",
"validate": "Bekræft",
"validation_success": "Valideret med succes"
"validation_success": "Valideret med succes",
"bcc_dest_format": "BCC-destination skal være en enkelt gyldig e-mail-adresse.<br>Hvis du har brug for at sende en kopi til flere adresser, kan du oprette et alias og bruge det her.",
"app_passwd_protocols": "Tilladte protokoller for app adgangskode",
"tags": "Tag's"
},
"admin": {
"access": "Adgang",
@ -313,7 +316,8 @@
"verify": "Verificere",
"yes": "&#10003;",
"ip_check_opt_in": "Opt-In for brug af tredjepartstjeneste <strong>ipv4.mailcow.email</strong> og <strong>ipv6.mailcow.email</strong> til at finde eksterne IP-adresser.",
"queue_unban": "unban"
"queue_unban": "unban",
"admins": "Administratorer"
},
"danger": {
"access_denied": "Adgang nægtet eller ugyldig formular data",
@ -1044,7 +1048,7 @@
"spamfilter_table_empty": "Intet data at vise",
"spamfilter_table_remove": "slet",
"spamfilter_table_rule": "Regl",
"spamfilter_wl": "Hvisliste",
"spamfilter_wl": "Hvidliste",
"spamfilter_wl_desc": "Hvidlistede e-mail-adresser til <b>aldrig</b> at klassificeres som spam. Wildcards kan bruges. Et filter anvendes kun på direkte aliaser (aliaser med en enkelt målpostkasse) eksklusive catch-aliaser og selve en postkasse.",
"spamfilter_yellow": "Gul: denne besked kan være spam, vil blive tagget som spam og flyttes til din junk-mappe",
"status": "Status",

View File

@ -175,10 +175,12 @@
"empty": "Keine Einträge vorhanden",
"excludes": "Diese Empfänger ausschließen",
"f2b_ban_time": "Bannzeit in Sekunden",
"f2b_ban_time_increment": "Bannzeit erhöht sich mit jedem Bann",
"f2b_blacklist": "Blacklist für Netzwerke und Hosts",
"f2b_filter": "Regex-Filter",
"f2b_list_info": "Ein Host oder Netzwerk auf der Blacklist wird immer eine Whitelist-Einheit überwiegen. <b>Die Aktualisierung der Liste dauert einige Sekunden.</b>",
"f2b_max_attempts": "Max. Versuche",
"f2b_max_ban_time": "Maximale Bannzeit in Sekunden",
"f2b_netban_ipv4": "Netzbereich für IPv4-Banns (8-32)",
"f2b_netban_ipv6": "Netzbereich für IPv6-Banns (8-128)",
"f2b_parameters": "Fail2ban-Parameter",

View File

@ -177,10 +177,12 @@
"empty": "No results",
"excludes": "Excludes these recipients",
"f2b_ban_time": "Ban time (s)",
"f2b_ban_time_increment": "Ban time is incremented with each ban",
"f2b_blacklist": "Blacklisted networks/hosts",
"f2b_filter": "Regex filters",
"f2b_list_info": "A blacklisted host or network will always outweigh a whitelist entity. <b>List updates will take a few seconds to be applied.</b>",
"f2b_max_attempts": "Max. attempts",
"f2b_max_ban_time": "Max. ban time (s)",
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
"f2b_parameters": "Fail2ban parameters",

View File

@ -141,9 +141,11 @@
"empty": "Sin resultados",
"excludes": "Excluye a estos destinatarios",
"f2b_ban_time": "Tiempo de restricción (s)",
"f2b_ban_time_increment": "Tiempo de restricción se incrementa con cada restricción",
"f2b_blacklist": "Redes y hosts en lista negra",
"f2b_list_info": "Un host o red en lista negra siempre superará a una entidad de la lista blanca. <b>Las actualizaciones de la lista tardarán unos segundos en aplicarse.</b>",
"f2b_max_attempts": "Max num. de intentos",
"f2b_max_ban_time": "Max tiempo de restricción (s)",
"f2b_netban_ipv4": "Tamaño de subred IPv4 para aplicar la restricción (8-32)",
"f2b_netban_ipv6": "Tamaño de subred IPv6 para aplicar la restricción (8-128)",
"f2b_parameters": "Parametros Fail2ban",

View File

@ -24,7 +24,7 @@
"spam_policy": "Liste Noire/Liste Blanche",
"spam_score": "Score SPAM",
"syncjobs": "Tâches de synchronisation",
"tls_policy": "Police TLS",
"tls_policy": "Politique TLS",
"unlimited_quota": "Quota illimité pour les boites de courriel",
"domain_desc": "Modifier la description du domaine",
"domain_relayhost": "Changer le relais pour un domaine",
@ -106,7 +106,8 @@
"validate": "Valider",
"validation_success": "Validation réussie",
"bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici.",
"tags": "Etiquettes"
"tags": "Etiquettes",
"app_passwd_protocols": "Protocoles autorisés pour le mot de passe de l'application"
},
"admin": {
"access": "Accès",
@ -172,10 +173,12 @@
"empty": "Aucun résultat",
"excludes": "Exclure ces destinataires",
"f2b_ban_time": "Durée du bannissement (s)",
"f2b_ban_time_increment": "Durée du bannissement est augmentée à chaque bannissement",
"f2b_blacklist": "Réseaux/Domaines sur Liste Noire",
"f2b_filter": "Filtre(s) Regex",
"f2b_list_info": "Un hôte ou un réseau sur liste noire l'emportera toujours sur une entité de liste blanche. <b>L'application des mises à jour de liste prendra quelques secondes.</b>",
"f2b_max_attempts": "Nb max. de tentatives",
"f2b_max_ban_time": "Max. durée du bannissement (s)",
"f2b_netban_ipv4": "Taille du sous-réseau IPv4 pour l'application du bannissement (8-32)",
"f2b_netban_ipv6": "Taille du sous-réseau IPv6 pour l'application du bannissement (8-128)",
"f2b_parameters": "Paramètres Fail2ban",
@ -321,7 +324,9 @@
"admins": "Administrateurs",
"api_read_only": "Accès lecture-seule",
"password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules",
"password_policy_numbers": "Doit contenir au moins un chiffre"
"password_policy_numbers": "Doit contenir au moins un chiffre",
"ip_check": "Vérification IP",
"ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>"
},
"danger": {
"access_denied": "Accès refusé ou données de formulaire non valides",
@ -440,7 +445,12 @@
"username_invalid": "Le nom d'utilisateur %s ne peut pas être utilisé",
"validity_missing": "Veuillez attribuer une période de validité",
"value_missing": "Veuillez fournir toutes les valeurs",
"yotp_verification_failed": "La vérification Yubico OTP a échoué : %s"
"yotp_verification_failed": "La vérification Yubico OTP a échoué : %s",
"webauthn_authenticator_failed": "L'authentificateur selectionné est introuvable",
"demo_mode_enabled": "Le mode de démonstration est activé",
"template_exists": "La template %s existe déja",
"template_id_invalid": "Le numéro de template %s est invalide",
"template_name_invalid": "Le nom de la template est invalide"
},
"debug": {
"chart_this_server": "Graphique (ce serveur)",
@ -578,7 +588,7 @@
"unchanged_if_empty": "Si non modifié, laisser en blanc",
"username": "Nom d'utilisateur",
"validate_save": "Valider et sauver",
"lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut).",
"lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut)",
"mailbox_relayhost_info": "S'applique uniquement à la boîte aux lettres et aux alias directs, remplace le relayhost du domaine."
},
"footer": {
@ -1081,9 +1091,12 @@
"username": "Nom d'utilisateur",
"verify": "Vérification",
"waiting": "En attente",
"week": "Semaine",
"week": "semaine",
"weekly": "Hebdomadaire",
"weeks": "semaines"
"weeks": "semaines",
"months": "mois",
"year": "année",
"years": "années"
},
"warning": {
"cannot_delete_self": "Impossible de supprimer lutilisateur connecté",

View File

@ -175,10 +175,12 @@
"empty": "Nessun risultato",
"excludes": "Esclude questi destinatari",
"f2b_ban_time": "Tempo di blocco (s)",
"f2b_ban_time_increment": "Tempo di blocco aumenta ad ogni blocco",
"f2b_blacklist": "Host/reti in blacklist",
"f2b_filter": "Filtri Regex",
"f2b_list_info": "Un host oppure una rete in blacklist, avrà sempre un peso maggiore rispetto ad una in whitelist. <b>L'aggiornamento della lista richiede alcuni secondi per la sua entrata in azione.</b>",
"f2b_max_attempts": "Tentativi massimi",
"f2b_max_ban_time": "Tempo massimo di blocco (s)",
"f2b_netban_ipv4": "IPv4 subnet size to apply ban on (8-32)",
"f2b_netban_ipv6": "IPv6 subnet size to apply ban on (8-128)",
"f2b_parameters": "Parametri Fail2ban",

View File

@ -168,10 +168,12 @@
"empty": "Geen resultaten",
"excludes": "Exclusief",
"f2b_ban_time": "Verbanningstijd (s)",
"f2b_ban_time_increment": "Verbanningstijd wordt verhoogd met elk verbanning",
"f2b_blacklist": "Netwerken/hosts op de blacklist",
"f2b_filter": "Regex-filters",
"f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. <b>Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.</b>",
"f2b_max_attempts": "Maximaal aantal pogingen",
"f2b_max_ban_time": "Maximaal verbanningstijd (s)",
"f2b_netban_ipv4": "Voer de IPv4-subnetgrootte in waar de verbanning van kracht moet zijn (8-32)",
"f2b_netban_ipv6": "Voer de IPv6-subnetgrootte in waar de verbanning van kracht moet zijn (8-128)",
"f2b_parameters": "Fail2ban",

View File

@ -12,6 +12,14 @@
<label for="f2b_ban_time">{{ lang.admin.f2b_ban_time }}:</label>
<input type="number" class="form-control" id="f2b_ban_time" name="ban_time" value="{{ f2b_data.ban_time }}" required>
</div>
<div class="mb-4">
<label for="f2b_max_ban_time">{{ lang.admin.f2b_max_ban_time }}:</label>
<input type="number" class="form-control" id="f2b_max_ban_time" name="max_ban_time" value="{{ f2b_data.max_ban_time }}" required>
</div>
<div class="mb-4">
<input class="form-check-input" type="checkbox" value="1" name="ban_time_increment" id="f2b_ban_time_increment" {% if f2b_data.ban_time_increment == 1 %}checked{% endif %}>
<label class="form-check-label" for="f2b_ban_time_increment">{{ lang.admin.f2b_ban_time_increment }}</label>
</div>
<div class="mb-4">
<label for="f2b_max_attempts">{{ lang.admin.f2b_max_attempts }}:</label>
<input type="number" class="form-control" id="f2b_max_attempts" name="max_attempts" value="{{ f2b_data.max_attempts }}" required>

View File

@ -76,7 +76,7 @@ services:
- clamd
rspamd-mailcow:
image: mailcow/rspamd:1.92
image: mailcow/rspamd:1.93
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
@ -106,7 +106,7 @@ services:
- rspamd
php-fpm-mailcow:
image: mailcow/phpfpm:1.82
image: mailcow/phpfpm:1.83
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on:
- redis-mailcow
@ -169,7 +169,7 @@ services:
- phpfpm
sogo-mailcow:
image: mailcow/sogo:1.115
image: mailcow/sogo:1.116
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@ -191,7 +191,7 @@ services:
volumes:
- ./data/hooks/sogo:/hooks:Z
- ./data/conf/sogo/:/etc/sogo/:z
- ./data/web/inc/init_db.inc.php:/init_db.inc.php:Z
- ./data/web/inc/init_db.inc.php:/init_db.inc.php:z
- ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z
- ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z
- ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z
@ -425,7 +425,7 @@ services:
- acme
netfilter-mailcow:
image: mailcow/netfilter:1.51
image: mailcow/netfilter:1.52
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
@ -510,7 +510,7 @@ services:
- watchdog
dockerapi-mailcow:
image: mailcow/dockerapi:2.01
image: mailcow/dockerapi:2.02
security_opt:
- label=disable
restart: always

View File

@ -205,8 +205,8 @@ DBUSER=mailcow
# Please use long, random alphanumeric strings (A-Za-z0-9)
DBPASS=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 | head -c 28)
DBROOT=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 | head -c 28)
DBPASS=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
DBROOT=$(LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
# ------------------------------
# HTTP/S Bindings

View File

@ -19,7 +19,7 @@ read -r -p "Are you sure you want to reset the mailcow administrator account? [y
response=${response,,} # tolower
if [[ "$response" =~ ^(yes|y)$ ]]; then
echo -e "\nWorking, please wait..."
random=$(</dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-16})
random=$(</dev/urandom tr -dc _A-Z-a-z-0-9 2> /dev/null | head -c${1:-16})
password=$(docker exec -it $(docker ps -qf name=dovecot-mailcow) doveadm pw -s SSHA256 -p ${random} | tr -d '\r')
docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM admin WHERE username='admin';"
docker exec -it $(docker ps -qf name=mysql-mailcow) mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DELETE FROM domain_admins WHERE username='admin';"

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
NEXTCLOUD_VERSION=25.0.4
NEXTCLOUD_VERSION=26.0.0
echo -ne "Checking prerequisites..."
sleep 1
@ -122,7 +122,7 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
&& chmod +x ./data/web/nextcloud/occ
echo -e "\033[33mCreating 'nextcloud' database...\033[0m"
NC_DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
NC_DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 2> /dev/null | head -c 28)
NC_DBUSER=nextcloud
NC_DBNAME=nextcloud
@ -138,7 +138,7 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
echo ""
echo -e "\033[33mInstalling Nextcloud...\033[0m"
ADMIN_NC_PASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
ADMIN_NC_PASS=$(</dev/urandom tr -dc A-Za-z0-9 2> /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"

View File

@ -176,18 +176,19 @@ remove_obsolete_nginx_ports() {
}
detect_docker_compose_command(){
if ! [ "${DOCKER_COMPOSE_VERSION}" == "native" ] && ! [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
if docker compose > /dev/null 2>&1; then
if docker compose version --short | grep "2." > /dev/null 2>&1; then
DOCKER_COMPOSE_VERSION=native
COMPOSE_COMMAND="docker compose"
echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' $SCRIPT_DIR/mailcow.conf
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[31mPlease update/install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
exit 1
fi
elif docker-compose > /dev/null 2>&1; then
@ -197,26 +198,60 @@ if ! [ "${DOCKER_COMPOSE_VERSION}" == "native" ] && ! [ "${DOCKER_COMPOSE_VERSIO
COMPOSE_COMMAND="docker-compose"
echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' $SCRIPT_DIR/mailcow.conf
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[31mPlease update/install regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
echo -e "\e[31mPlease update/install regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
exit 1
fi
fi
else
echo -e "\e[31mCannot find Docker Compose.\e[0m"
echo -e "\e[31mPlease install it regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
exit 1
fi
elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then
COMPOSE_COMMAND="docker compose"
# Check if Native Compose works and has not been deleted
if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
# IF it not exists/work anymore try the other command
COMPOSE_COMMAND="docker-compose"
if ! $COMPOSE_COMMAND > /dev/null 2>&1 || ! $COMPOSE_COMMAND --version | grep "^2." > /dev/null 2>&1; then
# IF it cannot find Standalone in > 2.X, then script stops
echo -e "\e[31mCannot find Docker Compose or the Version is lower then 2.X.X.\e[0m"
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
exit 1
fi
# If it finds the standalone Plugin it will use this instead and change the mailcow.conf Variable accordingly
echo -e "\e[31mFound different Docker Compose Version then declared in mailcow.conf!\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable from native to standalone\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' $SCRIPT_DIR/mailcow.conf
sleep 2
fi
elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
COMPOSE_COMMAND="docker-compose"
# Check if Standalone Compose works and has not been deleted
if ! $COMPOSE_COMMAND > /dev/null 2>&1 && ! $COMPOSE_COMMAND --version > /dev/null 2>&1 | grep "^2." > /dev/null 2>&1; then
# IF it not exists/work anymore try the other command
COMPOSE_COMMAND="docker compose"
if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
# IF it cannot find Native in > 2.X, then script stops
echo -e "\e[31mCannot find Docker Compose.\e[0m"
echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
exit 1
fi
# If it finds the native Plugin it will use this instead and change the mailcow.conf Variable accordingly
echo -e "\e[31mFound different Docker Compose Version then declared in mailcow.conf!\e[0m"
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable from standalone to native\e[0m"
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' $SCRIPT_DIR/mailcow.conf
sleep 2
fi
fi
}
@ -326,8 +361,12 @@ while (($#)); do
echo -e "\e[32mRunning in forced mode...\e[0m"
FORCE=y
;;
-d|--dev)
echo -e "\e[32mRunning in Developer mode...\e[0m"
DEV=y
;;
--help|-h)
echo './update.sh [-c|--check, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -h|--help]
echo './update.sh [-c|--check, --ours, --gc, --nightly, --prefetch, --skip-start, --skip-ping-check, --stable, -f|--force, -d|--dev, -h|--help]
-c|--check - Check for updates and exit (exit codes => 0: update available, 3: no updates)
--ours - Use merge strategy option "ours" to solve conflicts in favor of non-mailcow code (local changes over remote changes), not recommended!
@ -338,6 +377,7 @@ while (($#)); do
--skip-ping-check - Skip ICMP Check to public DNS resolvers (Use it only if you´ve blocked any ICMP Connections to your mailcow machine)
--stable - Switch your mailcow updates to the stable (master) branch. Default unless you changed it with --nightly.
-f|--force - Force update, do not ask questions
-d|--dev - Enables Developer Mode (No Checkout of update.sh for tests)
'
exit 1
esac
@ -597,7 +637,7 @@ for option in ${CONFIG_ARRAY[@]}; do
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Password hash algorithm' >> mailcow.conf
echo '# Only certain password hash algorithm are supported. For a fully list of supported schemes,' >> mailcow.conf
echo '# see https://mailcow.github.io/mailcow-dockerized-docs/models/model-passwd/' >> mailcow.conf
echo '# see https://docs.mailcow.email/models/model-passwd/' >> mailcow.conf
echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
fi
elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then
@ -617,7 +657,7 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# Optional: Leave empty for none' >> mailcow.conf
echo '# This value is only used on first order!' >> mailcow.conf
echo '# Setting it at a later point will require the following steps:' >> mailcow.conf
echo '# https://mailcow.github.io/mailcow-dockerized-docs/troubleshooting/debug-reset_tls/' >> mailcow.conf
echo '# https://docs.mailcow.email/troubleshooting/debug-reset_tls/' >> mailcow.conf
echo 'ACME_CONTACT=' >> mailcow.conf
fi
elif [[ ${option} == "WEBAUTHN_ONLY_TRUSTED_VENDORS" ]]; then
@ -727,6 +767,7 @@ elif [ $NEW_BRANCH == "nightly" ] && [ $CURRENT_BRANCH != "nightly" ]; then
git checkout -f ${BRANCH}
fi
if [ ! $DEV ]; then
echo -e "\e[32mChecking for newer update script...\e[0m"
SHA1_1=$(sha1sum update.sh)
git fetch origin #${BRANCH}
@ -737,6 +778,7 @@ if [[ ${SHA1_1} != ${SHA1_2} ]]; then
chmod +x update.sh
exit 2
fi
fi
if [ ! $FORCE ]; then
read -r -p "Are you sure you want to update mailcow: dockerized? All containers will be stopped. [y/N] " response
@ -902,9 +944,6 @@ else
echo -e "\e[33mCannot determine current git repository version...\e[0m"
fi
# Set DOCKER_COMPOSE_VERSION
sed -i 's/^DOCKER_COMPOSE_VERSION=$/DOCKER_COMPOSE_VERSION='$DOCKER_COMPOSE_VERSION'/g' mailcow.conf
if [[ ${SKIP_START} == "y" ]]; then
echo -e "\e[33mNot starting mailcow, please run \"$COMPOSE_COMMAND up -d --remove-orphans\" to start mailcow.\e[0m"
else