[Netfilter] New ban method, allow to set blacklists

master
André 2018-04-25 10:54:13 +02:00
parent a6810b81c6
commit 05e026db3a
14 changed files with 241 additions and 72 deletions

View File

@ -2,7 +2,7 @@ FROM alpine:3.7
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
RUN apk add -U python2 python-dev py-pip gcc musl-dev iptables ip6tables \
&& pip2 install --upgrade python-iptables redis ipaddress \
&& pip2 install --upgrade python-iptables==0.12.0 redis ipaddress \
&& apk del python-dev py2-pip gcc
COPY server.py /

View File

@ -24,6 +24,8 @@ RULES[4] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=(
RULES[5] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
RULES[6] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)'
def refresh_f2boptions():
global f2boptions
if not r.get('F2B_OPTIONS'):
f2boptions = {}
f2boptions['ban_time'] = int
@ -43,7 +45,8 @@ else:
f2boptions = json.loads(r.get('F2B_OPTIONS'))
except ValueError, e:
print 'Error loading F2B options: F2B_OPTIONS is not json'
raise SystemExit(1)
global quit_now
quit_now = True
if r.exists('F2B_LOG'):
r.rename('F2B_LOG', 'NETFILTER_LOG')
@ -52,7 +55,28 @@ bans = {}
log = {}
quit_now = False
def checkChainOrder():
filter4_table = iptc.Table(iptc.Table.FILTER)
filter6_table = iptc.Table6(iptc.Table6.FILTER)
for f in [filter4_table, filter6_table]:
forward_chain = iptc.Chain(f, 'FORWARD')
for position, item in enumerate(forward_chain.rules):
if item.target.name == 'MAILCOW':
mc_position = position
if item.target.name == 'DOCKER':
docker_position = position
if 'mc_position' in locals() and 'docker_position' in locals():
if int(mc_position) > int(docker_position):
log['time'] = int(round(time.time()))
log['priority'] = 'crit'
log['message'] = 'Error in chain order, restarting container'
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
print 'Error in chain order, restarting container...'
global quit_now
quit_now = True
def ban(address):
refresh_f2boptions()
BAN_TIME = int(f2boptions['ban_time'])
MAX_ATTEMPTS = int(f2boptions['max_attempts'])
RETRY_WINDOW = int(f2boptions['retry_window'])
@ -100,8 +124,7 @@ def ban(address):
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
print 'Banning %s for %d minutes' % (net, BAN_TIME / 60)
if type(ip) is ipaddress.IPv4Address:
for c in ['INPUT', 'FORWARD']:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), c)
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
rule = iptc.Rule()
rule.src = net
target = iptc.Target(rule, "REJECT")
@ -109,8 +132,7 @@ def ban(address):
if rule not in chain.rules:
chain.insert_rule(rule)
else:
for c in ['INPUT', 'FORWARD']:
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), c)
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), 'MAILCOW')
rule = iptc.Rule6()
rule.src = net
target = iptc.Target(rule, "REJECT")
@ -139,8 +161,7 @@ def unban(net):
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
print 'Unbanning %s' % net
if type(ipaddress.ip_network(net.decode('ascii'))) is ipaddress.IPv4Network:
for c in ['INPUT', 'FORWARD']:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), c)
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
rule = iptc.Rule()
rule.src = net
target = iptc.Target(rule, "REJECT")
@ -148,8 +169,7 @@ def unban(net):
if rule in chain.rules:
chain.delete_rule(rule)
else:
for c in ['INPUT', 'FORWARD']:
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), c)
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), 'MAILCOW')
rule = iptc.Rule6()
rule.src = net
target = iptc.Target(rule, "REJECT")
@ -173,6 +193,27 @@ def clear():
print 'Clearing all bans'
for net in bans.copy():
unban(net)
filter4_table = iptc.Table(iptc.Table.FILTER)
filter6_table = iptc.Table6(iptc.Table6.FILTER)
for filter_table in [filter4_table, filter6_table]:
filter_table.autocommit = False
forward_chain = iptc.Chain(filter_table, "FORWARD")
input_chain = iptc.Chain(filter_table, "INPUT")
mailcow_chain = iptc.Chain(filter_table, "MAILCOW")
if mailcow_chain in filter_table.chains:
for rule in mailcow_chain.rules:
mailcow_chain.delete_rule(rule)
for rule in forward_chain.rules:
if rule.target.name == 'MAILCOW':
forward_chain.delete_rule(rule)
for rule in input_chain.rules:
if rule.target.name == 'MAILCOW':
input_chain.delete_rule(rule)
filter_table.delete_chain("MAILCOW")
filter_table.commit()
filter_table.refresh()
filter_table.autocommit = True
r.delete('F2B_ACTIVE_BANS')
pubsub.unsubscribe()
def watch():
@ -226,6 +267,8 @@ def snat(snat_target):
def autopurge():
while not quit_now:
checkChainOrder()
refresh_f2boptions()
BAN_TIME = f2boptions['ban_time']
MAX_ATTEMPTS = f2boptions['max_attempts']
QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
@ -238,16 +281,59 @@ def autopurge():
unban(net)
time.sleep(10)
def cleanPrevious():
print "Cleaning previously cached bans"
F2B_ACTIVE_BANS = r.hgetall('F2B_ACTIVE_BANS')
if F2B_ACTIVE_BANS:
for net in F2B_ACTIVE_BANS:
unban(str(net))
def initChain():
print "Initializing mailcow netfilter chain"
# IPv4
if not iptc.Chain(iptc.Table(iptc.Table.FILTER), "MAILCOW") in iptc.Table(iptc.Table.FILTER).chains:
iptc.Table(iptc.Table.FILTER).create_chain("MAILCOW")
for c in ['FORWARD', 'INPUT']:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), c)
rule = iptc.Rule()
rule.src = '0.0.0.0/0'
rule.dst = '0.0.0.0/0'
target = iptc.Target(rule, "MAILCOW")
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
# IPv6
if not iptc.Chain(iptc.Table6(iptc.Table6.FILTER), "MAILCOW") in iptc.Table6(iptc.Table6.FILTER).chains:
iptc.Table6(iptc.Table6.FILTER).create_chain("MAILCOW")
for c in ['FORWARD', 'INPUT']:
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), c)
rule = iptc.Rule6()
rule.src = '::/0'
rule.dst = '::/0'
target = iptc.Target(rule, "MAILCOW")
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
# Apply blacklist
BLACKLIST = r.hgetall('F2B_BLACKLIST')
if BLACKLIST:
for bl_key in BLACKLIST:
if type(ipaddress.ip_network(bl_key.decode('ascii'), strict=False)) is ipaddress.IPv4Network:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
rule = iptc.Rule()
rule.src = bl_key
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
else:
chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), 'MAILCOW')
rule = iptc.Rule6()
rule.src = bl_key
target = iptc.Target(rule, "REJECT")
rule.target = target
if rule not in chain.rules:
chain.insert_rule(rule)
if __name__ == '__main__':
cleanPrevious()
# In case a previous session was killed without cleanup
clear()
# Reinit MAILCOW chain
initChain()
watch_thread = Thread(target=watch)
watch_thread.daemon = True

View File

@ -365,10 +365,15 @@ $tfa_data = get_tfa();
<input type="number" class="form-control" id="netban_ipv6" name="netban_ipv6" value="<?=$f2b_data['netban_ipv6'];?>" required>
</div>
</div>
<p class="help-block"><?=$lang['admin']['f2b_list_info'];?></p>
<div class="form-group">
<label for="whitelist"><?=$lang['admin']['f2b_whitelist'];?>:</label>
<textarea class="form-control" id="whitelist" name="whitelist" rows="5"><?=$f2b_data['whitelist'];?></textarea>
</div>
<div class="form-group">
<label for="blacklist"><?=$lang['admin']['f2b_blacklist'];?>:</label>
<textarea class="form-control" id="blacklist" name="blacklist" rows="5"><?=$f2b_data['blacklist'];?></textarea>
</div>
<button class="btn btn-default" id="add_item" data-id="f2b" data-api-url='edit/fail2ban' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
</form>
</div>

View File

@ -13,10 +13,10 @@ function fail2ban($_action, $_data = null) {
$wl = $redis->hGetAll('F2B_WHITELIST');
if (is_array($wl)) {
foreach ($wl as $key => $value) {
$tmp_data[] = $key;
$tmp_wl_data[] = $key;
}
if (isset($tmp_data)) {
$f2b_options['whitelist'] = implode(PHP_EOL, $tmp_data);
if (isset($tmp_wl_data)) {
$f2b_options['whitelist'] = implode(PHP_EOL, $tmp_wl_data);
}
else {
$f2b_options['whitelist'] = "";
@ -25,6 +25,21 @@ function fail2ban($_action, $_data = null) {
else {
$f2b_options['whitelist'] = "";
}
$bl = $redis->hGetAll('F2B_BLACKLIST');
if (is_array($bl)) {
foreach ($bl as $key => $value) {
$tmp_bl_data[] = $key;
}
if (isset($tmp_bl_data)) {
$f2b_options['blacklist'] = implode(PHP_EOL, $tmp_bl_data);
}
else {
$f2b_options['blacklist'] = "";
}
}
else {
$f2b_options['blacklist'] = "";
}
}
catch (RedisException $e) {
$_SESSION['return'] = array(
@ -50,6 +65,8 @@ function fail2ban($_action, $_data = null) {
$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']);
$wl = (isset($_data['whitelist'])) ? $_data['whitelist'] : $is_now['whitelist'];
$bl = (isset($_data['blacklist'])) ? $_data['blacklist'] : $is_now['blacklist'];
}
else {
$_SESSION['return'] = array(
@ -58,7 +75,6 @@ function fail2ban($_action, $_data = null) {
);
return false;
}
$wl = $_data['whitelist'];
$f2b_options = array();
$f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
$f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
@ -70,6 +86,7 @@ function fail2ban($_action, $_data = null) {
try {
$redis->Set('F2B_OPTIONS', json_encode($f2b_options));
$redis->Del('F2B_WHITELIST');
$redis->Del('F2B_BLACKLIST');
if(!empty($wl)) {
$wl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $wl));
if (is_array($wl_array)) {
@ -84,6 +101,20 @@ function fail2ban($_action, $_data = null) {
}
}
}
if(!empty($bl)) {
$bl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $bl));
if (is_array($bl_array)) {
foreach ($bl_array as $bl_item) {
$cidr = explode('/', $bl_item);
if (filter_var($cidr[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && (!isset($cidr[1]) || ($cidr[1] >= 0 && $cidr[1] <= 32))) {
$redis->hSet('F2B_BLACKLIST', $bl_item, 1);
}
elseif (filter_var($cidr[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && (!isset($cidr[1]) || ($cidr[1] >= 0 && $cidr[1] <= 128))) {
$redis->hSet('F2B_BLACKLIST', $bl_item, 1);
}
}
}
}
}
catch (RedisException $e) {
$_SESSION['return'] = array(

View File

@ -185,7 +185,6 @@ $lang['header']['mailcow_settings'] = 'Konfiguration';
$lang['header']['administration'] = 'Administration';
$lang['header']['mailboxes'] = 'Mailboxen';
$lang['header']['user_settings'] = 'Benutzereinstellungen';
$lang['header']['logged_in_as_logout_dual'] = 'Eingeloggt als <b>%s <span class="text-info">[%s]</span></b>';
$lang['mailbox']['domain'] = 'Domain';
$lang['mailbox']['spam_aliases'] = 'Temp. Alias';
$lang['mailbox']['alias'] = 'Alias';
@ -213,6 +212,8 @@ $lang['mailbox']['in_use'] = 'Prozentualer Gebrauch';
$lang['mailbox']['msg_num'] = 'Anzahl Nachrichten';
$lang['mailbox']['remove'] = 'Entfernen';
$lang['mailbox']['edit'] = 'Bearbeiten';
$lang['mailbox']['no_record'] = 'Kein Eintrag für Objekt %s';
$lang['mailbox']['no_record_single'] = 'Kein Eintrag';
$lang['mailbox']['add_domain'] = 'Domain hinzufügen';
$lang['mailbox']['add_domain_alias'] = 'Domain-Alias hinzufügen';
$lang['mailbox']['add_mailbox'] = 'Mailbox hinzufügen';
@ -366,6 +367,7 @@ $lang['tfa']['enter_qr_code'] = "Falls Sie den angezeigten QR-Code nicht scannen
$lang['tfa']['confirm_totp_token'] = "Bitte bestätigen Sie die Änderung durch Eingabe eines generierten Tokens";
$lang['admin']['no_new_rows'] = 'Keine weiteren Zeilen vorhanden';
$lang['admin']['additional_rows'] = ' zusätzliche Zeilen geladen'; // parses to 'n additional rows were added'
$lang['admin']['private_key'] = 'Private Key';
$lang['admin']['import'] = 'Importieren';
$lang['admin']['import_private_key'] = 'Private Key importieren';
@ -410,9 +412,12 @@ $lang['admin']['access'] = 'Zugang';
$lang['admin']['no_record'] = 'Kein Eintrag';
$lang['admin']['filter_table'] = 'Tabelle Filtern';
$lang['admin']['empty'] = 'Keine Einträge vorhanden';
$lang['admin']['time'] = 'Zeit';
$lang['admin']['priority'] = 'Gewichtung';
$lang['admin']['refresh'] = 'Neu laden';
$lang['admin']['to_top'] = 'Nach oben';
$lang['admin']['in_use_by'] = 'Verwendet von';
$lang['admin']['message'] = 'Nachricht';
$lang['admin']['forwarding_hosts'] = 'Weiterleitungs-Hosts';
$lang['admin']['forwarding_hosts_hint'] = 'Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt, optional kann er aber in den Spam-Ordner einsortiert werden. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem mailcow-Server eingerichtet wurde.';
$lang['admin']['forwarding_hosts_add_hint'] = 'Sie können entweder IPv4/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.';
@ -529,3 +534,4 @@ $lang['mailbox']['recipient_map_info'] = 'Empfängerumschreibung ersetzen den Em
$lang['mailbox']['recipient_map_old'] = 'Original Empfänger';
$lang['mailbox']['recipient_map_new'] = 'Neuer Empfänger';
$lang['mailbox']['add_recipient_map_entry'] = 'Empfängerumschreibung hinzufügen';
$lang['mailbox']['add_sender_map_entry'] = 'Senderumschreibung hinzufügen';

View File

@ -127,7 +127,7 @@ $lang['admin']['spamfilter'] = 'Spam filter';
$lang['user']['spamfilter_wl'] = 'Whitelist';
$lang['user']['spamfilter_wl_desc'] = 'Whitelisted email addresses to <b>never</b> classify as spam. Wildcards may be used. A filter is only applied to direct aliases (aliases with a single target mailbox) exclulding catch-all aliases and a mailbox itself.';
$lang['user']['spamfilter_bl'] = 'Blacklist';
$lang['user']['spamfilter_bl_desc'] = 'Blacklisted email addresses to <b>always</b> classify as spam and reject. Wildcards may be used. A filter is only applied to direct aliases (aliases with a single target mailbox) exclulding catch-all aliases and a mailbox itself.';
$lang['user']['spamfilter_bl_desc'] = 'Blacklisted email addresses to <b>always</b> classify as spam and reject. Wildcards may be used. A filter is only applied to direct aliases (aliases with a single target mailbox) excluding catch-all aliases and a mailbox itself.';
$lang['user']['spamfilter_behavior'] = 'Rating';
$lang['user']['spamfilter_table_rule'] = 'Rule';
$lang['user']['spamfilter_table_action'] = 'Action';
@ -183,7 +183,6 @@ $lang['header']['mailcow_settings'] = 'Configuration';
$lang['header']['administration'] = 'Administration';
$lang['header']['mailboxes'] = 'Mailboxes';
$lang['header']['user_settings'] = 'User settings';
$lang['header']['logged_in_as_logout_dual'] = 'Logged in as <b>%s <span class="text-info">[%s]</span></b>';
$lang['mailbox']['domain'] = 'Domain';
$lang['mailbox']['spam_aliases'] = 'Temp. alias';
$lang['mailbox']['multiple_bookings'] = 'Multiple bookings';
@ -211,11 +210,14 @@ $lang['mailbox']['in_use'] = 'In use (%)';
$lang['mailbox']['msg_num'] = 'Message #';
$lang['mailbox']['remove'] = 'Remove';
$lang['mailbox']['edit'] = 'Edit';
$lang['mailbox']['no_record'] = 'No record for object %s';
$lang['mailbox']['no_record_single'] = 'No record';
$lang['mailbox']['add_domain'] = 'Add domain';
$lang['mailbox']['add_domain_alias'] = 'Add domain alias';
$lang['mailbox']['add_mailbox'] = 'Add mailbox';
$lang['mailbox']['add_resource'] = 'Add resource';
$lang['mailbox']['add_alias'] = 'Add alias';
$lang['mailbox']['add_domain_record_first'] = 'Please add a domain first';
$lang['mailbox']['empty'] = 'No results';
$lang['mailbox']['toggle_all'] = 'Toggle all';
$lang['mailbox']['quick_actions'] = 'Actions';
@ -233,7 +235,11 @@ $lang['info']['no_action'] = 'No action applicable';
$lang['edit']['syncjob'] = 'Edit sync job';
$lang['edit']['username'] = 'Username';
$lang['edit']['client_id'] = 'Client ID';
$lang['edit']['client_secret'] = 'Client secret';
$lang['edit']['scope'] = 'Scope';
$lang['edit']['grant_types'] = 'Grant types';
$lang['edit']['redirect_uri'] = 'Redirect/Callback URL';
$lang['edit']['hostname'] = 'Hostname';
$lang['edit']['encryption'] = 'Encryption';
$lang['edit']['maxage'] = 'Maximum age of messages in days that will be polled from remote<br><small>(0 = ignore age)</small>';
@ -364,6 +370,7 @@ $lang['tfa']['enter_qr_code'] = "Your TOTP code if your device cannot scan QR co
$lang['tfa']['confirm_totp_token'] = "Please confirm your changes by entering the generated token";
$lang['admin']['no_new_rows'] = 'No further rows available';
$lang['admin']['additional_rows'] = ' additional rows were added'; // parses to 'n additional rows were added'
$lang['admin']['private_key'] = 'Private key';
$lang['admin']['import'] = 'Import';
$lang['admin']['import_private_key'] = 'Import private key';
@ -374,6 +381,8 @@ $lang['admin']['f2b_retry_window'] = 'Retry window (s) for max. attempts';
$lang['admin']['f2b_netban_ipv4'] = 'IPv4 subnet size to apply ban on (8-32)';
$lang['admin']['f2b_netban_ipv6'] = 'IPv6 subnet size to apply ban on (8-128)';
$lang['admin']['f2b_whitelist'] = 'Whitelisted networks/hosts';
$lang['admin']['f2b_blacklist'] = 'Blacklisted networks/hosts';
$lang['admin']['f2b_list_info'] = 'A blacklisted host or network will always outweigh a whitelist entity. Blacklist records are created at boot-time of the container. Whitelist records are read each time a ban is about to be applied.';
$lang['admin']['search_domain_da'] = 'Search domains';
$lang['admin']['r_inactive'] = 'Inactive restrictions';
$lang['admin']['r_active'] = 'Active restrictions';
@ -409,6 +418,9 @@ $lang['admin']['access'] = 'Access';
$lang['admin']['no_record'] = 'No record';
$lang['admin']['filter_table'] = 'Filter table';
$lang['admin']['empty'] = 'No results';
$lang['admin']['time'] = 'Time';
$lang['admin']['priority'] = 'Priority';
$lang['admin']['message'] = 'Message';
$lang['admin']['refresh'] = 'Refresh';
$lang['admin']['to_top'] = 'Back to top';
$lang['admin']['in_use_by'] = 'In use by';
@ -528,3 +540,12 @@ $lang['mailbox']['recipient_map_info'] = 'Recipient maps are used to replace the
$lang['mailbox']['recipient_map_old'] = 'Original recipient';
$lang['mailbox']['recipient_map_new'] = 'New recipient';
$lang['mailbox']['add_recipient_map_entry'] = 'Add recipient map';
$lang['mailbox']['add_sender_map_entry'] = 'Add sender map';
$lang['oauth2']['scope_ask_permission'] = 'An application asked for the following permissions';
$lang['oauth2']['profile'] = 'Profile';
$lang['oauth2']['profile_desc'] = 'View personal information: username, full name, created, modified, active';
$lang['oauth2']['permit'] = 'Authorize application';
$lang['oauth2']['authorize_app'] = 'Authorize application';
$lang['oauth2']['deny'] = 'Deny';
$lang['oauth2']['access_denied'] = 'Please login as mailbox owner to grant access via OAuth2.';

View File

@ -159,6 +159,7 @@ $lang['mailbox']['in_use'] = 'En uso (%)';
$lang['mailbox']['msg_num'] = 'Mensaje #';
$lang['mailbox']['remove'] = 'Eliminar';
$lang['mailbox']['edit'] = 'Editar';
$lang['mailbox']['no_record'] = 'Sin registro';
$lang['mailbox']['add_domain'] = 'Agregar dominio';
$lang['mailbox']['add_domain_alias'] = 'Agregar alias de dominio';
$lang['mailbox']['add_mailbox'] = 'Agregar buzón';

View File

@ -167,7 +167,6 @@ $lang['header']['mailcow_settings'] = "Configuration";
$lang['header']['administration'] = "Administration";
$lang['header']['mailboxes'] = "Boîtes de courriel";
$lang['header']['user_settings'] = "Paramètres utilisateur";
$lang['header']['logged_in_as_logout_dual'] = "Connecté en tant que <b>%s <span class=\"text-info\">[%s]</span></b>";
$lang['mailbox']['domain'] = "Domaine";
$lang['mailbox']['spam_aliases'] = "Alias temp.";
$lang['mailbox']['multiple_bookings'] = "Réservations multiples";
@ -195,11 +194,14 @@ $lang['mailbox']['in_use'] = "Utilisation (%)";
$lang['mailbox']['msg_num'] = "Message";
$lang['mailbox']['remove'] = "Retirer";
$lang['mailbox']['edit'] = "Éditer";
$lang['mailbox']['no_record'] = "Aucun enregistrement pour l'objet %s";
$lang['mailbox']['no_record_single'] = "Aucun enregistrement";
$lang['mailbox']['add_domain'] = "Ajouter un domaine";
$lang['mailbox']['add_domain_alias'] = "Ajouter un alias de domaine";
$lang['mailbox']['add_mailbox'] = "Ajouter une boîte de courriel";
$lang['mailbox']['add_resource'] = "Ajouter une ressource";
$lang['mailbox']['add_alias'] = "Ajouter un alias";
$lang['mailbox']['add_domain_record_first'] = "Merci de d'abord ajouter un domaine";
$lang['mailbox']['empty'] = "Aucun résultat";
$lang['mailbox']['toggle_all'] = "Basculer tout";
$lang['mailbox']['quick_actions'] = "Actions";
@ -362,6 +364,9 @@ $lang['admin']['access'] = "Accès";
$lang['admin']['no_record'] = "Aucun enregistrement";
$lang['admin']['filter_table'] = "Table de filtrage";
$lang['admin']['empty'] = "Aucun résultat";
$lang['admin']['time'] = "Temps";
$lang['admin']['priority'] = "Priorité";
$lang['admin']['message'] = "Message";
$lang['admin']['refresh'] = "Rafraîchir";
$lang['admin']['to_top'] = "Retour en haut";
$lang['admin']['in_use_by'] = "Utilisé par";

View File

@ -170,7 +170,6 @@ $lang['header']['mailcow_settings'] = 'Configurazione';
$lang['header']['administration'] = 'Amministrazione';
$lang['header']['mailboxes'] = 'Caselle';
$lang['header']['user_settings'] = 'Impostazioni utente';
$lang['header']['logged_in_as_logout_dual'] = 'Accesso come <b>%s <span class="text-info">[%s]</span></b>';
$lang['mailbox']['domain'] = 'Dominio';
$lang['mailbox']['spam_aliases'] = 'Alias temporanei';
$lang['mailbox']['multiple_bookings'] = 'Multiple bookings';
@ -198,11 +197,14 @@ $lang['mailbox']['in_use'] = 'In uso (%)';
$lang['mailbox']['msg_num'] = 'Messaggio #';
$lang['mailbox']['remove'] = 'Rimuovi';
$lang['mailbox']['edit'] = 'Modifica';
$lang['mailbox']['no_record'] = 'Nessun record per l\' oggetto %s';
$lang['mailbox']['no_record_single'] = 'Nessun record';
$lang['mailbox']['add_domain'] = 'Aggiungi Dominio';
$lang['mailbox']['add_domain_alias'] = 'Aggiungi alias Dominio';
$lang['mailbox']['add_mailbox'] = 'Aggiungi casella mail';
$lang['mailbox']['add_resource'] = 'Aggiungi risorsa';
$lang['mailbox']['add_alias'] = 'Aggiungi alias';
$lang['mailbox']['add_domain_record_first'] = 'Perfavore aggiungi il dominio prima';
$lang['mailbox']['empty'] = 'Nessun risultato';
$lang['mailbox']['toggle_all'] = 'Inverti tutti';
$lang['mailbox']['quick_actions'] = 'Azione veloce';

View File

@ -154,6 +154,7 @@ $lang['mailbox']['in_use'] = 'In gebruik (%)';
$lang['mailbox']['msg_num'] = 'Berichten #';
$lang['mailbox']['remove'] = 'Verwijder';
$lang['mailbox']['edit'] = 'Wijzig';
$lang['mailbox']['no_record'] = 'Geen vermelding';
$lang['mailbox']['add_domain'] = 'Toevoegen domein';
$lang['mailbox']['add_domain_alias'] = 'Toevoegen domein-alias';
$lang['mailbox']['add_mailbox'] = 'Toevoegen postvak';

View File

@ -172,7 +172,6 @@ $lang['header']['mailcow_settings'] = 'Konfiguracja';
$lang['header']['administration'] = 'Administrowanie';
$lang['header']['mailboxes'] = 'Skrzynki';
$lang['header']['user_settings'] = 'Ustawienia użytkownika';
$lang['header']['logged_in_as_logout_dual'] = 'Zalogowano jako <b>%s <span class="text-info">[%s]</span></b>';
$lang['mailbox']['domain'] = 'Domena';
$lang['mailbox']['spam_aliases'] = 'Alias tymczasowy';
$lang['mailbox']['multiple_bookings'] = 'Wielokrotne rejestracje';
@ -200,11 +199,14 @@ $lang['mailbox']['in_use'] = 'W użyciu (%)';
$lang['mailbox']['msg_num'] = 'Wiadomość #';
$lang['mailbox']['remove'] = 'Usuń';
$lang['mailbox']['edit'] = 'Edytuj';
$lang['mailbox']['no_record'] = 'Brak rekordu dla obiektu %s';
$lang['mailbox']['no_record_single'] = 'Brak rekordu';
$lang['mailbox']['add_domain'] = 'Dodaj domenę';
$lang['mailbox']['add_domain_alias'] = 'Dodaj alias domeny';
$lang['mailbox']['add_mailbox'] = 'Dodaj skrzynkę';
$lang['mailbox']['add_resource'] = 'Dodaj zasób';
$lang['mailbox']['add_alias'] = 'Dodaj alias';
$lang['mailbox']['add_domain_record_first'] = 'Proszę najpierw dodać domenę';
$lang['mailbox']['empty'] = 'Brak wyników';
$lang['mailbox']['toggle_all'] = 'Włącz wszystkie';
$lang['mailbox']['quick_actions'] = 'Szybkie działania';
@ -364,6 +366,9 @@ $lang['admin']['access'] = 'Dostęp';
$lang['admin']['no_record'] = 'Brak rekordu';
$lang['admin']['filter_table'] = 'Tabela filtru';
$lang['admin']['empty'] = 'Brak wyników';
$lang['admin']['time'] = 'Czas';
$lang['admin']['priority'] = 'Priorytet';
$lang['admin']['message'] = 'Wiadomość';
$lang['admin']['refresh'] = 'Odśwież';
$lang['admin']['forwarding_hosts'] = 'Hosty przekazujące';
$lang['admin']['forwarding_hosts_hint'] = 'Wiadomości przychodzące od hostów wyszczególnionych tutaj są akceptowane bezwarunkowo. Dane hosty nie są następnie sprawdzane względem list DNSBL lub poddawane metodzie szarych list. Otrzymany od nich spam nie jest nigdy odrzucany, ale opcjonalnie może zostać umieszczony w folderze spam. Najpopularniejsze zastosowanie dla takiego rozwiązania to wyszczególnienie serwerów poczty, na których ustawiono przekierowanie poczty przychodzącej na Twój serwer Mailcow.';

View File

@ -146,6 +146,7 @@ $lang['mailbox']['in_use'] = 'Em uso (%)';
$lang['mailbox']['msg_num'] = 'Mensagens';
$lang['mailbox']['remove'] = 'Remover';
$lang['mailbox']['edit'] = 'Alterar';
$lang['mailbox']['no_record'] = 'Nenhum registro';
$lang['mailbox']['add_domain'] = 'Adicionar Domínio';
$lang['mailbox']['add_domain_alias'] = 'Adicionar Apelido de Domínio';
$lang['mailbox']['add_mailbox'] = 'Adicionar Conta de Email';

View File

@ -168,7 +168,6 @@ $lang['header']['mailcow_settings'] = 'Конфигурация';
$lang['header']['administration'] = 'Администрирование';
$lang['header']['mailboxes'] = 'Почтовые ящики';
$lang['header']['user_settings'] = 'Настройки пользователя';
$lang['header']['logged_in_as_logout_dual'] = 'Вы вошли как <b>%s <span class="text-info">[%s]</span></b>';
$lang['mailbox']['domain'] = 'Домен';
$lang['mailbox']['spam_aliases'] = 'Временный псевдоним';
$lang['mailbox']['multiple_bookings'] = 'Multiple bookings';
@ -196,11 +195,14 @@ $lang['mailbox']['in_use'] = 'Использовано (%)';
$lang['mailbox']['msg_num'] = 'Письма #';
$lang['mailbox']['remove'] = 'Удалить';
$lang['mailbox']['edit'] = 'Изменить';
$lang['mailbox']['no_record'] = 'Нет записей для объекта %s';
$lang['mailbox']['no_record_single'] = 'Нет записей';
$lang['mailbox']['add_domain'] = 'Добавить домен';
$lang['mailbox']['add_domain_alias'] = 'Добавить псевдоним домена';
$lang['mailbox']['add_mailbox'] = 'Добавить почтовый ящик';
$lang['mailbox']['add_resource'] = 'Добавить ресурс';
$lang['mailbox']['add_alias'] = 'Добавить псевдоним';
$lang['mailbox']['add_domain_record_first'] = 'Пожалуйста, сначала добавьте домен';
$lang['mailbox']['empty'] = 'Нет результатов';
$lang['mailbox']['toggle_all'] = 'Выбрать все';
$lang['mailbox']['quick_actions'] = 'Действия';
@ -363,6 +365,9 @@ $lang['admin']['access'] = 'Доступ к';
$lang['admin']['no_record'] = 'Нет записей';
$lang['admin']['filter_table'] = 'Поиск';
$lang['admin']['empty'] = 'Нет результатов';
$lang['admin']['time'] = 'Время';
$lang['admin']['priority'] = 'Приоритет';
$lang['admin']['message'] = 'Письма';
$lang['admin']['refresh'] = 'Обновить';
$lang['admin']['to_top'] = 'Вернуться к началу';
$lang['admin']['in_use_by'] = 'Используемый в';

View File

@ -320,7 +320,7 @@ services:
- acme
netfilter-mailcow:
image: mailcow/netfilter:1.11
image: mailcow/netfilter:1.12
build: ./data/Dockerfiles/netfilter
stop_grace_period: 30s
depends_on: