diff --git a/.gitignore b/.gitignore index 9dcc83b0..9ea8b05b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ data/assets/ssl/* data/web/.well-known/acme-challenge data/conf/rspamd/local.d/* data/conf/rspamd/override.d/* +!data/conf/nginx/dynmaps.conf +!data/conf/nginx/site.conf +data/conf/nginx/*.conf diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..c7614088 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +sudo: required +services: +- docker +script: +- echo 'Europe/Berlin' | MAILCOW_HOSTNAME=build.mailcow ./generate_config.sh +- docker-compose pull --ignore-pull-failures --parallel +- docker-compose build +- docker login --username=$DOCKER_HUB_USERNAME --password=$DOCKER_HUB_PASSWORD +- docker-compose push +branches: + only: + - master +env: + global: + - secure: MpxpTwD7f0CNEVLitSpVmocK7O9r+BwFE1deEHK4AlQo/oc9cOlhGe1EL3mx9zbglPmjlDg/8kMUGv6vSirIabfBo9Szjps76bHckFr9lr2Ykkg0e29oC8pgPpSXD1eY/1ZIN/FvIkxpUFLETo1okS/j9q/A0DCGFmti0n3EoMORsgRz9CpNAiEh0zpSd6+euPAGHuczuCrDuO84my9bIOCjA/+aPunHNeXiuM8yIM2SxCSyGtIKT0+jvquIvLF58VxivysXBlRfhDn8fhB09nXA2Ru/derYQACfcmNSn9Pd4bDpebPJW5B9H/XA8xjb58uKinUlncbAMB/QnxoT75j9YRWJZRSQ+34XNYP6ZgK9soZ2TC6djQyEKTUu45Kp/1s+poSn42m9jytJJTmmK0KxsZTRcC8JD5nrjIMZWPUNNTwC5L4+I7ZRWg2WooK3LNyq1Ng8Hn6W77wSgsvAJw2HD3Lx58AprGUhHuBeaIZRuSN9aKwZrl9vKQJLqPnOp/nF2EC6kot5HYYtcotGtETXPUDih21gWD5ZM2BqVqYfQQnJnNMgeYmMdj6QQuTFqhuNJf7hXRIRkTnD3j1gDOLKQZazW0+N2JE8XWDFwi6fKScDsxT85lJti9HmzHa7+k4RVHmUYuDgRoPuzUgjWHvPsiz3/Z8WQ9JYpH84S8w= + - secure: fWzZisT6nGDNL4lf6tXB07eFG2drgBakHxzdF/NFVvzuP861RFR6omuL+ED0PgXrEHDJBxaBLv52je8irmUXrAH1CNr7T8DWiZo/h5h609Uzr+38T1NnIu4krL0Wo6/CDwlLKnzqTq9yBIZLQSHVJmo8AOpo1JPIi2ajodqj9ZfmAxDQTQl+G6zvQjtqIkYHsHY7A44Rto0f14ykn7w2S82Jn6Ry89VNI5V1WEO3sMpM/XekNP/HokNcRIuntL/0+kuLvTJ5akGoTjBQxSnSW95opzPeGky74HRU2obExJYqKvF0VfVJRNAqejwjIiFIbbjqV0Sk5391kFuhuBErQQDM1bOHGdxZ41HsJH29qNWIl7C33Yl10qERoqecgsJ1N/bS2ZEmWqm/zQh5GClCXPvYmzEqMYsMGM3vjbKdjDlc1Wh2w/eFclsXN9LSXh1mc35rtj46frcT6e5Kof87AIfC9hTgDvk9kAsyjaHMkSHSZthbZXCIcsD8qriNm5UqfFBYD79mPIP1S2YMQ2jscCsjHOZgYVrcm0kzDF21J1w6H0Lo7d1jw37LYlegBdtLQ9gYgqY2D5m+nxWuVoD5FZmpR+5JGtK+ootyLFF8aiFoHXd4op1JCxRLjgkmnZKXzw3kTQSpE7oa7CgzchtQmK2nqcqla1b5Qk7ilVcjooo= diff --git a/data/Dockerfiles/acme/docker-entrypoint.sh b/data/Dockerfiles/acme/docker-entrypoint.sh index 9882e22f..f23efd08 100755 --- a/data/Dockerfiles/acme/docker-entrypoint.sh +++ b/data/Dockerfiles/acme/docker-entrypoint.sh @@ -36,6 +36,8 @@ else fi fi +[[ ! -f ${ACME_BASE}/dhparams.pem ]] && cp ${SSL_EXAMPLE}/dhparams.pem ${ACME_BASE}/dhparams.pem + while true; do if [[ "${SKIP_LETS_ENCRYPT}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then echo "SKIP_LETS_ENCRYPT=y, skipping Let's Encrypt..." @@ -44,7 +46,7 @@ while true; do declare -a SQL_DOMAIN_ARR declare -a VALIDATED_CONFIG_DOMAINS declare -a ADDITIONAL_VALIDATED_SAN - IFS=' ' read -r -a ADDITIONAL_SAN_ARR <<< "${ADDITIONAL_SAN}" + IFS=',' read -r -a ADDITIONAL_SAN_ARR <<< "${ADDITIONAL_SAN}" IPV4=$(curl -4s https://mailcow.email/ip.php) while read line; do diff --git a/data/Dockerfiles/fail2ban/logwatch.py b/data/Dockerfiles/fail2ban/logwatch.py index d2a8d52f..b7430988 100644 --- a/data/Dockerfiles/fail2ban/logwatch.py +++ b/data/Dockerfiles/fail2ban/logwatch.py @@ -1,6 +1,7 @@ #!/usr/bin/env python2 import re +import os import time import atexit import signal @@ -12,7 +13,12 @@ import redis import time import json -r = redis.StrictRedis(host='172.22.1.249', port=6379, db=0) +yes_regex = re.compile(r'([yY][eE][sS]|[yY])+$') +if re.search(yes_regex, os.getenv('SKIP_FAIL2BAN', 0)): + print "Skipping Fail2ban container..." + raise SystemExit + +r = redis.StrictRedis(host='172.22.1.249', decode_responses=True, port=6379, db=0) RULES = { 'mailcowdockerized_postfix-mailcow_1': 'warning: .*\[([0-9a-f\.:]+)\]: SASL .* authentication failed', 'mailcowdockerized_dovecot-mailcow_1': '-login: Disconnected \(auth failed, .*\): user=.*, method=.*, rip=([0-9a-f\.:]+),', @@ -32,6 +38,7 @@ def ban(address): BAN_TIME = int(r.get("F2B_BAN_TIME")) MAX_ATTEMPTS = int(r.get("F2B_MAX_ATTEMPTS")) RETRY_WINDOW = int(r.get("F2B_RETRY_WINDOW")) + WHITELIST = r.hgetall("F2B_WHITELIST") ip = ipaddress.ip_address(address.decode('ascii')) if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped: @@ -39,7 +46,19 @@ def ban(address): address = str(ip) if ip.is_private or ip.is_loopback: return - + + self_network = ipaddress.ip_network(address.decode('ascii')) + if WHITELIST: + for wl_key in WHITELIST: + wl_net = ipaddress.ip_network(wl_key.decode('ascii'), False) + if wl_net.overlaps(self_network): + log['time'] = int(round(time.time())) + log['priority'] = "info" + log['message'] = "Address %s is whitelisted by rule %s" % (self_network, wl_net) + r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) + print "Address %s is whitelisted by rule %s" % (self_network, wl_net) + return + net = ipaddress.ip_network((address + ('/24' if type(ip) is ipaddress.IPv4Address else '/64')).decode('ascii'), strict=False) net = str(net) @@ -48,10 +67,10 @@ def ban(address): active_window = RETRY_WINDOW else: active_window = time.time() - bans[net]['last_attempt'] - + bans[net]['attempts'] += 1 bans[net]['last_attempt'] = time.time() - + active_window = time.time() - bans[net]['last_attempt'] if bans[net]['attempts'] >= MAX_ATTEMPTS: @@ -66,6 +85,7 @@ def ban(address): else: subprocess.call(["ip6tables", "-I", "INPUT", "-s", net, "-j", "REJECT"]) subprocess.call(["ip6tables", "-I", "FORWARD", "-s", net, "-j", "REJECT"]) + r.hset("F2B_ACTIVE_BANS", "%s" % net, log['time'] + BAN_TIME) else: log['time'] = int(round(time.time())) log['priority'] = "warn" @@ -76,6 +96,13 @@ def ban(address): def unban(net): log['time'] = int(round(time.time())) log['priority'] = "info" + r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) + if not net in bans: + log['message'] = "%s is not banned, skipping unban and deleting from queue (if any)" % net + r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) + print "%s is not banned, skipping unban and deleting from queue (if any)" % net + r.hdel("F2B_QUEUE_UNBAN", "%s" % net) + return log['message'] = "Unbanning %s" % net r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) print "Unbanning %s" % net @@ -85,6 +112,8 @@ def unban(net): else: subprocess.call(["ip6tables", "-D", "INPUT", "-s", net, "-j", "REJECT"]) subprocess.call(["ip6tables", "-D", "FORWARD", "-s", net, "-j", "REJECT"]) + r.hdel("F2B_ACTIVE_BANS", "%s" % net) + r.hdel("F2B_QUEUE_UNBAN", "%s" % net) del bans[net] def quit(signum, frame): @@ -117,11 +146,15 @@ def autopurge(): while not quit_now: BAN_TIME = int(r.get("F2B_BAN_TIME")) MAX_ATTEMPTS = int(r.get("F2B_MAX_ATTEMPTS")) + QUEUE_UNBAN = r.hgetall("F2B_QUEUE_UNBAN") + if QUEUE_UNBAN: + for net in QUEUE_UNBAN: + unban(str(net)) for net in bans.copy(): if bans[net]['attempts'] >= MAX_ATTEMPTS: if time.time() - bans[net]['last_attempt'] > BAN_TIME: unban(net) - time.sleep(60) + time.sleep(30) if __name__ == '__main__': threads = [] diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua index db8a08d5..75f52892 100644 --- a/data/conf/rspamd/lua/rspamd.local.lua +++ b/data/conf/rspamd/lua/rspamd.local.lua @@ -7,9 +7,6 @@ rspamd_config.MAILCOW_AUTH = { end } -local redis_params -redis_params = rspamd_parse_redis_server('tag_settings') -if redis_params then rspamd_config:register_symbol({ name = 'TAG_MOO', type = 'postfilter', @@ -20,11 +17,6 @@ rspamd_config:register_symbol({ local tagged_rcpt = task:get_symbol("TAGGED_RCPT") local mailcow_domain = task:get_symbol("RCPT_MAILCOW_DOMAIN") - local user = task:get_recipients(0)[1]['user'] - local domain = task:get_recipients(0)[1]['domain'] - local rcpt = user .. '@' .. domain - - if tagged_rcpt and mailcow_domain then local tag = tagged_rcpt[1].options[1] rspamd_logger.infox("found tag: %s", tag) @@ -57,5 +49,5 @@ rspamd_config:register_symbol({ end, priority = 11 }) -end + diff --git a/data/web/admin.php b/data/web/admin.php index 22e7ee16..787129f0 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -269,6 +269,34 @@ $tfa_data = get_tfa(); + +
+
Fail2Ban parameters
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php deleted file mode 100644 index 3baab049..00000000 --- a/data/web/autodiscover.php +++ /dev/null @@ -1,164 +0,0 @@ - 'yes', - 'autodiscoverType' => 'activesync', - 'imap' => array( - 'server' => $mailcow_hostname, - 'port' => '993', - 'ssl' => 'on', - ), - 'smtp' => array( - 'server' => $mailcow_hostname, - 'port' => '465', - 'ssl' => 'on' - ), - 'activesync' => array( - 'url' => 'https://'.$mailcow_hostname.'/Microsoft-Server-ActiveSync' - ) -); - -if(file_exists('inc/vars.local.inc.php')) { - include_once 'inc/vars.local.inc.php'; -} - -/* ---------- DO NOT MODIFY ANYTHING BEYOND THIS LINE. IGNORE AT YOUR OWN RISK. ---------- */ - -error_reporting(0); - -$data = trim(file_get_contents("php://input")); - -// Desktop client needs IMAP, unless it's Outlook 2013 or higher on Windows -if (strpos($data, 'autodiscover/outlook/responseschema')) { // desktop client - $config['autodiscoverType'] = 'imap'; - if ($config['useEASforOutlook'] == 'yes' && - strpos($_SERVER['HTTP_USER_AGENT'], 'Windows NT') !== FALSE && // Windows - preg_match('/(Outlook|Office) (1[5-9]\.|[2-9]|1[0-9][0-9])/', $_SERVER['HTTP_USER_AGENT']) && // Outlook 2013 (version 15) or higher - strpos($_SERVER['HTTP_USER_AGENT'], 'MS Connectivity Analyzer') === FALSE // https://testconnectivity.microsoft.com doesn't support EAS for Outlook - ) { - $config['autodiscoverType'] = 'activesync'; - } -} - -$dsn = "$database_type:host=$database_host;dbname=$database_name"; -$opt = [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => false, -]; -$pdo = new PDO($dsn, $database_user, $database_pass, $opt); -$login_user = strtolower(trim($_SERVER['PHP_AUTH_USER'])); -$as = check_login($login_user, $_SERVER['PHP_AUTH_PW']); - -if (!isset($_SERVER['PHP_AUTH_USER']) OR $as !== "user") { - header('WWW-Authenticate: Basic realm=""'); - header('HTTP/1.0 401 Unauthorized'); - exit; -} else { - if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) { - if ($as === "user") { - header("Content-Type: application/xml"); - echo ''; - - if(!$data) { - list($usec, $sec) = explode(' ', microtime()); - echo ''; - echo ''; - echo '600Invalid Request'; - echo ''; - echo ''; - exit(0); - } - $discover = new SimpleXMLElement($data); - $email = $discover->Request->EMailAddress; - - if ($config['autodiscoverType'] == 'imap') { - ?> - - - - - - email - settings - - IMAP - - - off - - off - - on - - - SMTP - - - off - - off - - on - on - off - - - CalDAV - https:///SOGo/dav//Calendar - off - - - - CardDAV - https:///SOGo/dav//Contacts - off - - - - - prepare("SELECT `name` FROM `mailbox` WHERE `username`= :username"); - $stmt->execute(array(':username' => $username)); - $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC); - } - catch(PDOException $e) { - die("Failed to determine name from SQL"); - } - if (!empty($MailboxData['name'])) { - $displayname = utf8_encode($MailboxData['name']); - } - else { - $displayname = $email; - } - ?> - - en:en - - - - - - - - MobileSync - - - - - - - - - diff --git a/data/web/css/admin.css b/data/web/css/admin.css index 0253f33c..d2bb2489 100644 --- a/data/web/css/admin.css +++ b/data/web/css/admin.css @@ -11,6 +11,11 @@ table.footable>tbody>tr.footable-empty>td { .table-responsive { overflow: visible !important; } +@media screen and (max-width: 767px) { + .table-responsive { + overflow-x: scroll !important; + } +} body { overflow-y:scroll; } diff --git a/data/web/css/edit.css b/data/web/css/edit.css index 605a1010..fe4d9fff 100644 --- a/data/web/css/edit.css +++ b/data/web/css/edit.css @@ -11,6 +11,11 @@ table.footable>tbody>tr.footable-empty>td { .table-responsive { overflow: visible !important; } +@media screen and (max-width: 767px) { + .table-responsive { + overflow-x: scroll !important; + } +} .footer-add-item { display:block; text-align: center; diff --git a/data/web/css/mailbox.css b/data/web/css/mailbox.css index 8f711669..9f07debe 100644 --- a/data/web/css/mailbox.css +++ b/data/web/css/mailbox.css @@ -11,6 +11,11 @@ table.footable>tbody>tr.footable-empty>td { .table-responsive { overflow: visible !important; } +@media screen and (max-width: 767px) { + .table-responsive { + overflow-x: scroll !important; + } +} .footer-add-item { display:block; text-align: center; @@ -30,4 +35,3 @@ table.footable>tbody>tr.footable-empty>td { .inputMissingAttr { border-color: #FF4136; } - diff --git a/data/web/css/user.css b/data/web/css/user.css index 2222b624..07d4e745 100644 --- a/data/web/css/user.css +++ b/data/web/css/user.css @@ -11,6 +11,11 @@ table.footable>tbody>tr.footable-empty>td { .table-responsive { overflow: visible !important; } +@media screen and (max-width: 767px) { + .table-responsive { + overflow-x: scroll !important; + } +} .footer-add-item { display:block; text-align: center; diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 9498cf30..68a09531 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -229,6 +229,7 @@ function check_login($user, $pass) { } if (!isset($_SESSION['ldelay'])) { $_SESSION['ldelay'] = "0"; + error_log("Mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']); } elseif (!isset($_SESSION['mailcow_cc_username'])) { $_SESSION['ldelay'] = $_SESSION['ldelay']+0.5; @@ -1434,4 +1435,94 @@ function get_logs($container, $lines = 100) { } return false; } +function get_f2b_parameters() { + global $lang; + global $redis; + $data = array(); + if ($_SESSION['mailcow_cc_role'] != "admin") { + return false; + } + try { + $data['ban_time'] = $redis->Get('F2B_BAN_TIME'); + $data['max_attempts'] = $redis->Get('F2B_MAX_ATTEMPTS'); + $data['retry_window'] = $redis->Get('F2B_RETRY_WINDOW'); + $wl = $redis->hGetAll('F2B_WHITELIST'); + if (is_array($wl)) { + foreach ($wl as $key => $value) { + $tmp_data[] = $key; + } + $data['whitelist'] = implode(PHP_EOL, $tmp_data); + } + else { + $data['whitelist'] = ""; + } + } + catch (RedisException $e) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => 'Redis: '.$e + ); + return false; + } + return $data; +} +function edit_f2b_parameters($postarray) { + global $lang; + global $redis; + if ($_SESSION['mailcow_cc_role'] != "admin") { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['access_denied']) + ); + return false; + } + $is_now = get_f2b_parameters(); + if (!empty($is_now)) { + $ban_time = intval((isset($postarray['ban_time'])) ? $postarray['ban_time'] : $is_now['ban_time']); + $max_attempts = intval((isset($postarray['max_attempts'])) ? $postarray['max_attempts'] : $is_now['active_int']); + $retry_window = intval((isset($postarray['retry_window'])) ? $postarray['retry_window'] : $is_now['retry_window']); + } + else { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => sprintf($lang['danger']['access_denied']) + ); + return false; + } + $wl = $postarray['whitelist']; + $ban_time = ($ban_time < 60) ? 60 : $ban_time; + $max_attempts = ($max_attempts < 1) ? 1 : $max_attempts; + $retry_window = ($retry_window < 1) ? 1 : $retry_window; + try { + $redis->Set('F2B_BAN_TIME', $ban_time); + $redis->Set('F2B_MAX_ATTEMPTS', $max_attempts); + $redis->Set('F2B_RETRY_WINDOW', $retry_window); + $redis->Del('F2B_WHITELIST'); + if(!empty($wl)) { + $wl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $wl)); + if (is_array($wl_array)) { + foreach ($wl_array as $wl_item) { + $cidr = explode('/', $wl_item); + if (filter_var($cidr[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && (!isset($cidr[1]) || ($cidr[1] >= 0 && $cidr[1] <= 32))) { + $redis->hSet('F2B_WHITELIST', $wl_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_WHITELIST', $wl_item, 1); + } + } + } + } + } + catch (RedisException $e) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => 'Redis: '.$e + ); + return false; + } + $_SESSION['return'] = array( + 'type' => 'success', + 'msg' => 'Saved changes to Fail2ban configuration' + ); +} ?> diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php index f2d09c5c..7ee06c3a 100644 --- a/data/web/inc/vars.inc.php +++ b/data/web/inc/vars.inc.php @@ -17,6 +17,28 @@ $database_name = getenv('DBNAME'); // Other variables $mailcow_hostname = getenv('MAILCOW_HOSTNAME'); +// Autodiscover settings +$autodiscover_config = array( + // Enable the autodiscover service for Outlook desktop clients + 'useEASforOutlook' => 'yes', + // General autodiscover service type: "activesync" or "imap" + 'autodiscoverType' => 'activesync', + 'imap' => array( + 'server' => $mailcow_hostname, + 'port' => getenv('IMAPS_PORT'), + 'ssl' => 'on', + ), + 'smtp' => array( + 'server' => $mailcow_hostname, + 'port' => getenv('SMTPS_PORT'), + 'ssl' => 'on' + ), + 'activesync' => array( + 'url' => 'https://'.$mailcow_hostname.'/Microsoft-Server-ActiveSync' + ) +); + + // Where to go after adding and editing objects // Can be "form" or "previous" // "form" will stay in the current form, "previous" will redirect to previous page diff --git a/data/web/json_api.php b/data/web/json_api.php index b3b22a7d..d891f88d 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -1921,6 +1921,41 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u )); } break; + case "fail2ban": + // No items + if (isset($_POST['attr'])) { + $attr = (array)json_decode($_POST['attr'], true); + if (edit_f2b_parameters($attr) === false) { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Edit failed' + )); + } + exit(); + } + else { + if (isset($_SESSION['return'])) { + echo json_encode($_SESSION['return']); + } + else { + echo json_encode(array( + 'type' => 'success', + 'msg' => 'Task completed' + )); + } + } + } + else { + echo json_encode(array( + 'type' => 'error', + 'msg' => 'Incomplete post data' + )); + } + break; case "admin": // No items as there is only one admin if (isset($_POST['attr'])) { diff --git a/docker-compose.yml b/docker-compose.yml index 2c9f6666..8847c80f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -122,6 +122,14 @@ services: - DBUSER=${DBUSER} - DBPASS=${DBPASS} - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME} + - IMAP_PORT=${IMAP_PORT:-143} + - IMAPS_PORT=${IMAPS_PORT:-993} + - POP_PORT=${POP_PORT:-110} + - POPS_PORT=${POPS_PORT:-995} + - SIEVE_PORT=${SIEVE_PORT:-4190} + - SUBMISSION_PORT=${SUBMISSION_PORT:-587} + - SMTPS_PORT=${SMTPS_PORT:-465} + - SMTP_PORT=${SMTP_PORT:-25} restart: always logging: options: @@ -285,7 +293,7 @@ services: acme-mailcow: depends_on: - nginx-mailcow - image: mailcow/acme:1.4 + image: mailcow/acme:1.5 build: ./data/Dockerfiles/acme dns: - 172.22.1.254 @@ -311,7 +319,7 @@ services: - acme fail2ban-mailcow: - image: mailcow/fail2ban:1.2 + image: mailcow/fail2ban:1.3 build: ./data/Dockerfiles/fail2ban depends_on: - dovecot-mailcow @@ -323,6 +331,7 @@ services: privileged: true environment: - TZ=${TZ} + - SKIP_FAIL2BAN=${SKIP_FAIL2BAN:-no} network_mode: "host" dns: - 172.22.1.254 @@ -330,7 +339,6 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - /lib/modules:/lib/modules:ro - ipv6nat: image: robbertkl/ipv6nat restart: always diff --git a/generate_config.sh b/generate_config.sh index 43b46cfe..4cd17074 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -81,6 +81,8 @@ ADDITIONAL_SAN= # To never run acme-mailcow for Let's Encrypt, set this to y SKIP_LETS_ENCRYPT=n +# To never run fail2ban-mailcow +SKIP_FAIL2BAN=n EOF