Page MenuHomeWMGMC Issues

No OneTemporary

diff --git a/data/web/inc/ajax/dns_diagnostics.php b/data/web/inc/ajax/dns_diagnostics.php
index 54fc6cac..c6781cb6 100644
--- a/data/web/inc/ajax/dns_diagnostics.php
+++ b/data/web/inc/ajax/dns_diagnostics.php
@@ -1,480 +1,480 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/spf.inc.php';
define('state_good', '<span class="glyphicon glyphicon-ok text-success"></span>');
define('state_missing', '<span class="glyphicon glyphicon-remove text-danger"></span>');
define('state_nomatch', "?");
define('state_optional', " <sup>2</sup>");
if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin"|| $_SESSION['mailcow_cc_role'] == "domainadmin")) {
$alias_domains = array();
if (isset($_GET['domain'])) {
$domain_details = mailbox('get', 'domain_details', $_GET['domain']);
if ($domain_details !== false) {
$domain = $_GET['domain'];
$alias_domains = array_merge($alias_domains, mailbox('get', 'alias_domains', $domain));
}
else {
echo "No such domain in context";
exit();
}
}
$ch = curl_init('http://ip4.mailcow.email');
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
$ip = curl_exec($ch);
curl_close($ch);
$ch = curl_init('http://ip6.mailcow.email');
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
$ip6 = curl_exec($ch);
curl_close($ch);
$ptr = implode('.', array_reverse(explode('.', $ip))) . '.in-addr.arpa';
if (!empty($ip6)) {
$ip6_full = str_replace('::', str_repeat(':', 9-substr_count($ip6, ':')), $ip6);
$ip6_full = str_replace('::', ':0:', $ip6_full);
$ip6_full = str_replace('::', ':0:', $ip6_full);
$ptr6 = '';
foreach (explode(':', $ip6_full) as $part) {
$ptr6 .= str_pad($part, 4, '0', STR_PAD_LEFT);
}
$ptr6 = implode('.', array_reverse(str_split($ptr6, 1))) . '.ip6.arpa';
}
$https_port = strpos($_SERVER['HTTP_HOST'], ':');
if ($https_port === FALSE) {
$https_port = 443;
}
else {
$https_port = substr($_SERVER['HTTP_HOST'], $https_port+1);
}
if (!isset($autodiscover_config['sieve'])) {
$autodiscover_config['sieve'] = array(
'server' => $mailcow_hostname,
'port' => array_pop(explode(':', getenv('SIEVE_PORT')))
);
}
// Init records array
$spf_link = '<a href="https://en.wikipedia.org/wiki/Sender_Policy_Framework" target="_blank">SPF Record Syntax</a><br />';
$dmarc_link = '<a href="https://www.kitterman.com/dmarc/assistant.html" target="_blank">DMARC Assistant</a>';
$records = array();
if ($_SESSION['mailcow_cc_role'] == "admin") {
$records[] = array(
$mailcow_hostname,
'A',
$ip
);
$records[] = array(
$ptr,
'PTR',
$mailcow_hostname
);
if (!empty($ip6)) {
$records[] = array(
$mailcow_hostname,
'AAAA',
expand_ipv6($ip6)
);
$records[] = array(
$ptr6,
'PTR',
$mailcow_hostname
);
}
$records[] = array(
'_25._tcp.' . $autodiscover_config['smtp']['server'],
'TLSA',
generate_tlsa_digest($autodiscover_config['smtp']['server'], 25, 1)
);
}
$records[] = array(
$domain,
'MX',
$mailcow_hostname
);
if (!in_array($domain, $alias_domains)) {
$records[] = array(
'autodiscover.' . $domain,
'CNAME',
$mailcow_hostname
);
$records[] = array(
'_autodiscover._tcp.' . $domain,
'SRV',
$mailcow_hostname . ' ' . $https_port
);
$records[] = array(
'autoconfig.' . $domain,
'CNAME',
$mailcow_hostname
);
if ($domain_details['xmpp'] === 1 && isset($domain_details['xmpp_prefix'])) {
$records[] = array(
$domain_details['xmpp_prefix'] . '.' . $domain,
'CNAME',
$mailcow_hostname
);
$records[] = array(
'*.' . $domain_details['xmpp_prefix'] . '.' . $domain,
'CNAME',
$mailcow_hostname
);
$records[] = array(
'_xmpp-client._tcp.' . $domain_details['xmpp_prefix'] . '.' . $domain,
'SRV',
- $mailcow_hostname . ' ' . array_pop(explode(':', getenv('XMPP_C22_PORT')))
+ $mailcow_hostname . ' ' . array_pop(explode(':', getenv('XMPP_C2S_PORT')))
);
$records[] = array(
'_xmpp-server._tcp.' . $domain_details['xmpp_prefix'] . '.' . $domain,
'SRV',
$mailcow_hostname . ' ' . array_pop(explode(':', getenv('XMPP_S2S_PORT')))
);
}
}
$records[] = array(
$domain,
'TXT',
$spf_link,
state_optional
);
$records[] = array(
'_dmarc.' . $domain,
'TXT',
$dmarc_link,
state_optional
);
if (!empty($dkim = dkim('details', $domain))) {
$records[] = array(
$dkim['dkim_selector'] . '._domainkey.' . $domain,
'TXT',
$dkim['dkim_txt']
);
}
if (!in_array($domain, $alias_domains)) {
$current_records = dns_get_record('_pop3._tcp.' . $domain, DNS_SRV);
if (count($current_records) == 0 || $current_records[0]['target'] != '') {
if ($autodiscover_config['pop3']['tlsport'] != '110') {
$records[] = array(
'_pop3._tcp.' . $domain,
'SRV',
$autodiscover_config['pop3']['server'] . ' ' . $autodiscover_config['pop3']['tlsport']
);
}
}
else {
$records[] = array(
'_pop3._tcp.' . $domain,
'SRV',
'. 0'
);
}
$current_records = dns_get_record('_pop3s._tcp.' . $domain, DNS_SRV);
if (count($current_records) == 0 || $current_records[0]['target'] != '') {
if ($autodiscover_config['pop3']['port'] != '995') {
$records[] = array(
'_pop3s._tcp.' . $domain,
'SRV',
$autodiscover_config['pop3']['server'] . ' ' . $autodiscover_config['pop3']['port']
);
}
}
else {
$records[] = array(
'_pop3s._tcp.' . $domain,
'SRV',
'. 0'
);
}
if ($autodiscover_config['imap']['tlsport'] != '143') {
$records[] = array(
'_imap._tcp.' . $domain,
'SRV',
$autodiscover_config['imap']['server'] . ' ' . $autodiscover_config['imap']['tlsport']
);
}
if ($autodiscover_config['imap']['port'] != '993') {
$records[] = array(
'_imaps._tcp.' . $domain,
'SRV',
$autodiscover_config['imap']['server'] . ' ' . $autodiscover_config['imap']['port']
);
}
if ($autodiscover_config['smtp']['tlsport'] != '587') {
$records[] = array(
'_submission._tcp.' . $domain,
'SRV',
$autodiscover_config['smtp']['server'] . ' ' . $autodiscover_config['smtp']['tlsport']
);
}
if ($autodiscover_config['smtp']['port'] != '465') {
$records[] = array(
'_smtps._tcp.' . $domain,
'SRV',
$autodiscover_config['smtp']['server'] . ' ' . $autodiscover_config['smtp']['port']
);
}
if ($autodiscover_config['sieve']['port'] != '4190') {
$records[] = array(
'_sieve._tcp.' . $domain,
'SRV',
$autodiscover_config['sieve']['server'] . ' ' . $autodiscover_config['sieve']['port']
);
}
}
$record_types = array(
'A' => DNS_A,
'AAAA' => DNS_AAAA,
'CNAME' => DNS_CNAME,
'MX' => DNS_MX,
'PTR' => DNS_PTR,
'SRV' => DNS_SRV,
'TXT' => DNS_TXT,
);
$data_field = array(
'A' => 'ip',
'AAAA' => 'ipv6',
'CNAME' => 'target',
'MX' => 'target',
'PTR' => 'target',
'SRV' => 'data',
'TLSA' => 'data',
'TXT' => 'txt',
);
?>
<div class="table-responsive" id="dnstable">
<table class="table table-striped">
<tr>
<th><?=$lang['diagnostics']['dns_records_name'];?></th>
<th><?=$lang['diagnostics']['dns_records_type'];?></th>
<th><?=$lang['diagnostics']['dns_records_data'];?></th>
<th><?=$lang['diagnostics']['dns_records_status'];?></th>
</tr>
<?php
foreach ($records as &$record) {
$record[1] = strtoupper($record[1]);
$state = state_optional;
if ($record[1] == 'TLSA') {
$currents = dns_get_record($record[0], 52, $_, $_, TRUE);
foreach ($currents as &$current) {
$current['type'] = 'TLSA';
$current['cert_usage'] = hexdec(bin2hex($current['data']{0}));
$current['selector'] = hexdec(bin2hex($current['data']{1}));
$current['match_type'] = hexdec(bin2hex($current['data']{2}));
$current['cert_data'] = bin2hex(substr($current['data'], 3));
$current['data'] = $current['cert_usage'] . ' ' . $current['selector'] . ' ' . $current['match_type'] . ' ' . $current['cert_data'];
}
unset($current);
}
else {
$currents = dns_get_record($record[0], $record_types[$record[1]]);
if ($record[0] == $mailcow_hostname && ($record[1] == "A" || $record[1] == "AAAA")) {
if (!empty(dns_get_record($record[0], DNS_CNAME))) {
$currents[0]['ip'] = state_missing . ' <b>(CNAME)</b>';
$currents[0]['ipv6'] = state_missing . ' <b>(CNAME)</b>';
}
}
if ($record[1] == 'SRV') {
foreach ($currents as &$current) {
if ($current['target'] == '') {
$current['target'] = '.';
$current['port'] = '0';
}
$current['data'] = $current['target'] . ' ' . $current['port'];
}
unset($current);
}
elseif ($record[1] == 'TXT') {
foreach ($currents as &$current) {
unset($current);
}
unset($current);
}
elseif ($record[1] == 'AAAA') {
foreach ($currents as &$current) {
$current['ipv6'] = expand_ipv6($current['ipv6']);
}
}
}
if ($record[1] == 'CNAME' && count($currents) == 0) {
// A and AAAA are also valid instead of CNAME
$a = dns_get_record($record[0], DNS_A);
$cname = dns_get_record($record[2], DNS_A);
if (count($a) > 0 && count($cname) > 0) {
if ($a[0]['ip'] == $cname[0]['ip']) {
$currents = array(
array(
'host' => $record[0],
'class' => 'IN',
'type' => 'CNAME',
'target' => $record[2]
)
);
$aaaa = dns_get_record($record[0], DNS_AAAA);
$cname = dns_get_record($record[2], DNS_AAAA);
if (count($aaaa) == 0 || count($cname) == 0 || expand_ipv6($aaaa[0]['ipv6']) != expand_ipv6($cname[0]['ipv6'])) {
$currents[0]['target'] = expand_ipv6($aaaa[0]['ipv6']) . ' <sup>1</sup>';
}
}
else {
$currents = array(
array(
'host' => $record[0],
'class' => 'IN',
'type' => 'CNAME',
'target' => $a[0]['ip'] . ' <sup>1</sup>'
)
);
}
}
}
foreach ($currents as &$current) {
if ($current['type'] == 'TXT' &&
stripos($current['txt'], 'v=dmarc') === 0 &&
$record[2] == $dmarc_link) {
$current['txt'] = str_replace(' ', '', $current['txt']);
$state = $current[$data_field[$current['type']]] . state_optional;
}
elseif ($current['type'] == 'TXT' &&
stripos($current['txt'], 'v=spf') === 0 &&
$record[2] == $spf_link) {
$state = state_nomatch;
$rslt = get_spf_allowed_hosts($record[0], true);
if (in_array($ip, $rslt) && in_array(expand_ipv6($ip6), $rslt)) {
$state = state_good;
}
$state .= '<br />' . $current[$data_field[$current['type']]] . state_optional;
}
elseif ($current['type'] == 'TXT' &&
stripos($current['txt'], 'v=dkim') === 0 &&
stripos($record[2], 'v=dkim') === 0) {
preg_match('/v=DKIM1;.*k=rsa;.*p=([^;]*).*/i', $current[$data_field[$current['type']]], $dkim_matches_current);
preg_match('/v=DKIM1;.*k=rsa;.*p=([^;]*).*/i', $record[2], $dkim_matches_good);
if ($dkim_matches_current[1] == $dkim_matches_good[1]) {
$state = state_good;
}
}
elseif ($current['type'] != 'TXT' &&
isset($data_field[$current['type']]) && $state != state_good) {
$state = state_nomatch;
if ($current[$data_field[$current['type']]] == $record[2]) {
$state = state_good;
}
}
}
unset($current);
if (isset($record[3]) &&
$record[3] == state_optional &&
($state == state_missing || $state == state_nomatch)) {
$state = state_optional;
}
if ($state == state_nomatch) {
$state = array();
foreach ($currents as $current) {
$state[] = $current[$data_field[$current['type']]];
}
$state = implode('<br />', $state);
}
echo sprintf('
<tr>
<td>%s</td>
<td>%s</td>
<td class="dns-found">%s</td>
<td class="dns-recommended">%s</td>
</tr>', $record[0], $record[1], $record[2], $state);
$record[3] = explode('<br />', $state);
}
unset($record);
$dns_data = sprintf("\$ORIGIN %s.\n", $domain);
foreach ($records as $record) {
if ($domain == substr($record[0], -strlen($domain))) {
$label = substr($record[0], 0, -strlen($domain)-1);
$val = $record[2];
if (strlen($label) == 0) {
$label = "@";
}
$vals = array();
if (strpos($val, "<a") !== FALSE) {
if (is_array($record[3]) && count($record[3]) == 1 && $record[3][0] == state_optional) {
$record[3][0] = "**TODO**";
$label = ';' . $label;
}
foreach ($record[3] as $val) {
$val = str_replace(state_optional, '', $val);
$val = str_replace(state_good, '', $val);
if (strlen($val) > 0) {
$vals[] = sprintf("%s\tIN\t%s\t%s\n", $label, $record[1], $val);
}
}
}
else {
$vals[] = sprintf("%s\tIN\t%s\t%s\n", $label, $record[1], $val);
}
foreach ($vals as $val) {
$dns_data .= str_replace($domain, $domain . '.', $val);
}
}
}
?>
</table>
<a id='download-zonefile' data-zonefile="<?=base64_encode($dns_data);?>" download='<?=$_GET['domain'];?>.txt' type='text/csv'>Download</a>
<script>
var zonefile_dl_link = document.getElementById('download-zonefile');
var zonefile = atob(zonefile_dl_link.getAttribute('data-zonefile'));
var data = new Blob([zonefile]);
var download_zonefile_link = document.getElementById('download-zonefile');
download_zonefile_link.href = URL.createObjectURL(data);
</script>
</div>
<p class="help-block">
<sup>1</sup> <?=$lang['diagnostics']['cname_from_a'];?><br />
<sup>2</sup> <?=$lang['diagnostics']['optional'];?>
</p>
<?php
}
else {
echo "Session invalid";
exit();
}
?>
diff --git a/docker-compose.yml b/docker-compose.yml
index 77ea833c..d2ead1de 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,617 +1,617 @@
version: '2.1'
services:
unbound-mailcow:
image: mailcow/unbound:1.12
environment:
- TZ=${TZ}
volumes:
- ./data/hooks/unbound:/hooks:Z
- ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro,Z
restart: always
tty: true
networks:
mailcow-network:
ipv4_address: ${IPV4_NETWORK:-172.22.1}.254
aliases:
- unbound
mysql-mailcow:
image: mariadb:10.4
depends_on:
- unbound-mailcow
stop_grace_period: 45s
volumes:
- mysql-vol-1:/var/lib/mysql/:Z
- mysql-socket-vol-1:/var/run/mysqld/:z
- ./data/conf/mysql/:/etc/mysql/conf.d/:ro,Z
environment:
- TZ=${TZ}
- MYSQL_ROOT_PASSWORD=${DBROOT}
- MYSQL_DATABASE=${DBNAME}
- MYSQL_USER=${DBUSER}
- MYSQL_PASSWORD=${DBPASS}
- MYSQL_INITDB_SKIP_TZINFO=1
restart: always
ports:
- "${SQL_PORT:-127.0.0.1:13306}:3306"
networks:
mailcow-network:
aliases:
- mysql
redis-mailcow:
image: redis:5-alpine
volumes:
- redis-vol-1:/data/:Z
restart: always
ports:
- "${REDIS_PORT:-127.0.0.1:7654}:6379"
environment:
- TZ=${TZ}
networks:
mailcow-network:
ipv4_address: ${IPV4_NETWORK:-172.22.1}.249
aliases:
- redis
clamd-mailcow:
image: mailcow/clamd:1.38
restart: always
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment:
- TZ=${TZ}
- SKIP_CLAMD=${SKIP_CLAMD:-n}
volumes:
- ./data/conf/clamav/:/etc/clamav/:Z
networks:
mailcow-network:
aliases:
- clamd
rspamd-mailcow:
image: mailcow/rspamd:1.76
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
environment:
- TZ=${TZ}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64}
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
volumes:
- ./data/hooks/rspamd:/hooks:Z
- ./data/conf/rspamd/custom/:/etc/rspamd/custom:z
- ./data/conf/rspamd/override.d/:/etc/rspamd/override.d:Z
- ./data/conf/rspamd/local.d/:/etc/rspamd/local.d:Z
- ./data/conf/rspamd/plugins.d/:/etc/rspamd/plugins.d:Z
- ./data/conf/rspamd/lua/:/etc/rspamd/lua/:ro,Z
- ./data/conf/rspamd/rspamd.conf.local:/etc/rspamd/rspamd.conf.local:Z
- ./data/conf/rspamd/rspamd.conf.override:/etc/rspamd/rspamd.conf.override:Z
- rspamd-vol-1:/var/lib/rspamd:z
restart: always
hostname: rspamd
dns:
- ${IPV4_NETWORK:-172.22.1}.254
networks:
mailcow-network:
aliases:
- rspamd
php-fpm-mailcow:
image: mailcow/phpfpm:1.71
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on:
- redis-mailcow
volumes:
- ./data/hooks/phpfpm:/hooks:Z
- ./data/web:/web:z
- ./data/conf/rspamd/dynmaps:/dynmaps:ro,z
- ./data/conf/rspamd/custom/:/rspamd_custom_maps:z
- rspamd-vol-1:/var/lib/rspamd:z
- mysql-socket-vol-1:/var/run/mysqld/:z
- ./data/conf/sogo/:/etc/sogo/:z
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z
- ./data/conf/phpfpm/sogo-sso/:/etc/sogo-sso/:z
- ./data/conf/phpfpm/php-fpm.d/pools.conf:/usr/local/etc/php-fpm.d/z-pools.conf:Z
- ./data/conf/phpfpm/php-conf.d/opcache-recommended.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini:Z
- ./data/conf/phpfpm/php-conf.d/upload.ini:/usr/local/etc/php/conf.d/upload.ini:Z
- ./data/conf/phpfpm/php-conf.d/other.ini:/usr/local/etc/php/conf.d/zzz-other.ini:Z
- ./data/conf/dovecot/global_sieve_before:/global_sieve/before:z
- ./data/conf/dovecot/global_sieve_after:/global_sieve/after:z
- ./data/assets/templates:/tpls:z
- ./data/conf/ejabberd/autogen:/ejabberd/:z
- ./data/conf/nginx/:/etc/nginx/conf.d/:z
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment:
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
- LOG_LINES=${LOG_LINES:-9999}
- TZ=${TZ}
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- MAILCOW_PASS_SCHEME=${MAILCOW_PASS_SCHEME:-BLF-CRYPT}
- 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}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64}
- SUBMISSION_PORT=${SUBMISSION_PORT:-587}
- SMTPS_PORT=${SMTPS_PORT:-465}
- SMTP_PORT=${SMTP_PORT:-25}
- - XMPP_C22_PORT=${XMPP_C22_PORT:-5222}
+ - XMPP_C2S_PORT=${XMPP_C2S_PORT:-5222}
- XMPP_S2S_PORT=${XMPP_S2S_PORT:-5269}
- API_KEY=${API_KEY:-invalid}
- API_KEY_READ_ONLY=${API_KEY_READ_ONLY:-invalid}
- API_ALLOW_FROM=${API_ALLOW_FROM:-invalid}
- COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized}
- SKIP_SOLR=${SKIP_SOLR:-y}
- SKIP_CLAMD=${SKIP_CLAMD:-n}
- SKIP_SOGO=${SKIP_SOGO:-n}
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
- MASTER=${MASTER:-y}
restart: always
networks:
mailcow-network:
aliases:
- phpfpm
sogo-mailcow:
image: mailcow/sogo:1.95
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
- TZ=${TZ}
- LOG_LINES=${LOG_LINES:-9999}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- MAILCOW_PASS_SCHEME=${MAILCOW_PASS_SCHEME:-BLF-CRYPT}
- ACL_ANYONE=${ACL_ANYONE:-disallow}
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- SOGO_EXPIRE_SESSION=${SOGO_EXPIRE_SESSION:-480}
- SKIP_SOGO=${SKIP_SOGO:-n}
- MASTER=${MASTER:-y}
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
dns:
- ${IPV4_NETWORK:-172.22.1}.254
volumes:
- ./data/conf/sogo/:/etc/sogo/: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
- mysql-socket-vol-1:/var/run/mysqld/:z
- sogo-web-vol-1:/sogo_web:z
- sogo-userdata-backup-vol-1:/sogo_backup:Z
restart: always
networks:
mailcow-network:
ipv4_address: ${IPV4_NETWORK:-172.22.1}.248
aliases:
- sogo
dovecot-mailcow:
image: mailcow/dovecot:1.140
depends_on:
- mysql-mailcow
dns:
- ${IPV4_NETWORK:-172.22.1}.254
cap_add:
- NET_BIND_SERVICE
volumes:
- ./data/hooks/dovecot:/hooks:Z
- ./data/conf/dovecot:/etc/dovecot:z
- ./data/assets/ssl:/etc/ssl/mail/:ro,z
- ./data/conf/sogo/:/etc/sogo/:z
- ./data/conf/phpfpm/sogo-sso/:/etc/phpfpm/:z
- vmail-vol-1:/var/vmail:Z
- vmail-index-vol-1:/var/vmail_index:Z
- crypt-vol-1:/mail_crypt/:z
- ./data/conf/rspamd/custom/:/etc/rspamd/custom:z
- ./data/assets/templates:/templates:z
- rspamd-vol-1:/var/lib/rspamd:z
- mysql-socket-vol-1:/var/run/mysqld/:z
environment:
- DOVECOT_MASTER_USER=${DOVECOT_MASTER_USER:-}
- DOVECOT_MASTER_PASS=${DOVECOT_MASTER_PASS:-}
- LOG_LINES=${LOG_LINES:-9999}
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
- TZ=${TZ}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- MAILCOW_PASS_SCHEME=${MAILCOW_PASS_SCHEME:-BLF-CRYPT}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
- MAILDIR_GC_TIME=${MAILDIR_GC_TIME:-1440}
- ACL_ANYONE=${ACL_ANYONE:-disallow}
- SKIP_SOLR=${SKIP_SOLR:-y}
- MAILDIR_SUB=${MAILDIR_SUB:-}
- MASTER=${MASTER:-y}
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
- COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized}
ports:
- "${DOVEADM_PORT:-127.0.0.1:19991}:12345"
- "${IMAP_PORT:-143}:143"
- "${IMAPS_PORT:-993}:993"
- "${POP_PORT:-110}:110"
- "${POPS_PORT:-995}:995"
- "${SIEVE_PORT:-4190}:4190"
restart: always
tty: true
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
networks:
mailcow-network:
ipv4_address: ${IPV4_NETWORK:-172.22.1}.250
aliases:
- dovecot
postfix-mailcow:
image: mailcow/postfix:1.59
depends_on:
- mysql-mailcow
volumes:
- ./data/hooks/postfix:/hooks:Z
- ./data/conf/postfix:/opt/postfix/conf:z
- ./data/assets/ssl:/etc/ssl/mail/:ro,z
- postfix-vol-1:/var/spool/postfix:z
- crypt-vol-1:/var/lib/zeyple:z
- rspamd-vol-1:/var/lib/rspamd:z
- mysql-socket-vol-1:/var/run/mysqld/:z
environment:
- LOG_LINES=${LOG_LINES:-9999}
- TZ=${TZ}
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
cap_add:
- NET_BIND_SERVICE
ports:
- "${SMTP_PORT:-25}:25"
- "${SMTPS_PORT:-465}:465"
- "${SUBMISSION_PORT:-587}:587"
restart: always
dns:
- ${IPV4_NETWORK:-172.22.1}.254
networks:
mailcow-network:
aliases:
- postfix
memcached-mailcow:
image: memcached:alpine
restart: always
environment:
- TZ=${TZ}
networks:
mailcow-network:
aliases:
- memcached
nginx-mailcow:
depends_on:
- sogo-mailcow
- php-fpm-mailcow
- redis-mailcow
image: nginx:mainline-alpine
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/server_name.template > /etc/nginx/conf.d/server_name.active &&
envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active &&
. /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo_proxy_auth.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 &&
until ping ejabberd -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}
- TZ=${TZ}
- SKIP_SOGO=${SKIP_SOGO:-n}
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
volumes:
- ./data/web:/web:ro,z
- ./data/conf/rspamd/dynmaps:/dynmaps:ro,z
- ./data/assets/ssl/:/etc/ssl/mail/:ro,z
- ./data/conf/nginx/:/etc/nginx/conf.d/:z
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro,z
- sogo-web-vol-1:/usr/lib/GNUstep/SOGo/:z
ports:
- "${HTTPS_BIND:-:}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
- "${HTTP_BIND:-:}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
restart: always
networks:
mailcow-network:
aliases:
- nginx
acme-mailcow:
depends_on:
- nginx-mailcow
image: mailcow/acme:1.76
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment:
- LOG_LINES=${LOG_LINES:-9999}
- ADDITIONAL_SAN=${ADDITIONAL_SAN}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
- SKIP_LETS_ENCRYPT=${SKIP_LETS_ENCRYPT:-n}
- COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized}
- DIRECTORY_URL=${DIRECTORY_URL:-}
- ENABLE_SSL_SNI=${ENABLE_SSL_SNI:-n}
- SKIP_IP_CHECK=${SKIP_IP_CHECK:-n}
- SKIP_HTTP_VERIFICATION=${SKIP_HTTP_VERIFICATION:-n}
- ONLY_MAILCOW_HOSTNAME=${ONLY_MAILCOW_HOSTNAME:-n}
- LE_STAGING=${LE_STAGING:-n}
- TZ=${TZ}
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
- SNAT_TO_SOURCE=${SNAT_TO_SOURCE:-n}
- SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n}
volumes:
- ./data/web/.well-known/acme-challenge:/var/www/acme:z
- ./data/assets/ssl:/var/lib/acme/:z
- ./data/assets/ssl-example:/var/lib/ssl-example/:ro,Z
- mysql-socket-vol-1:/var/run/mysqld/:z
restart: always
networks:
mailcow-network:
aliases:
- acme
netfilter-mailcow:
image: mailcow/netfilter:1.38
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
- postfix-mailcow
- sogo-mailcow
- php-fpm-mailcow
- redis-mailcow
restart: always
privileged: true
environment:
- TZ=${TZ}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64}
- SNAT_TO_SOURCE=${SNAT_TO_SOURCE:-n}
- SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n}
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
network_mode: "host"
volumes:
- /lib/modules:/lib/modules:ro
watchdog-mailcow:
image: mailcow/watchdog:1.86
# Debug
#command: /watchdog.sh
dns:
- ${IPV4_NETWORK:-172.22.1}.254
tmpfs:
- /tmp
volumes:
- rspamd-vol-1:/var/lib/rspamd:z
- mysql-socket-vol-1:/var/run/mysqld/:z
- postfix-vol-1:/var/spool/postfix:z
- ./data/assets/ssl:/etc/ssl/mail/:ro,z
restart: always
environment:
- IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64}
- LOG_LINES=${LOG_LINES:-9999}
- TZ=${TZ}
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
- DBROOT=${DBROOT}
- USE_WATCHDOG=${USE_WATCHDOG:-n}
- WATCHDOG_NOTIFY_EMAIL=${WATCHDOG_NOTIFY_EMAIL}
- WATCHDOG_NOTIFY_BAN=${WATCHDOG_NOTIFY_BAN:-y}
- WATCHDOG_EXTERNAL_CHECKS=${WATCHDOG_EXTERNAL_CHECKS:-n}
- WATCHDOG_MYSQL_REPLICATION_CHECKS=${WATCHDOG_MYSQL_REPLICATION_CHECKS:-n}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-mailcow-dockerized}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- IP_BY_DOCKER_API=${IP_BY_DOCKER_API:-0}
- CHECK_UNBOUND=${CHECK_UNBOUND:-1}
- SKIP_CLAMD=${SKIP_CLAMD:-n}
- SKIP_LETS_ENCRYPT=${SKIP_LETS_ENCRYPT:-n}
- SKIP_SOGO=${SKIP_SOGO:-n}
- HTTPS_PORT=${HTTPS_PORT:-443}
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
- EXTERNAL_CHECKS_THRESHOLD=${EXTERNAL_CHECKS_THRESHOLD:-1}
- NGINX_THRESHOLD=${NGINX_THRESHOLD:-5}
- UNBOUND_THRESHOLD=${UNBOUND_THRESHOLD:-5}
- REDIS_THRESHOLD=${REDIS_THRESHOLD:-5}
- MYSQL_THRESHOLD=${MYSQL_THRESHOLD:-5}
- MYSQL_REPLICATION_THRESHOLD=${MYSQL_REPLICATION_THRESHOLD:-1}
- SOGO_THRESHOLD=${SOGO_THRESHOLD:-3}
- POSTFIX_THRESHOLD=${POSTFIX_THRESHOLD:-8}
- CLAMD_THRESHOLD=${CLAMD_THRESHOLD:-15}
- DOVECOT_THRESHOLD=${DOVECOT_THRESHOLD:-12}
- DOVECOT_REPL_THRESHOLD=${DOVECOT_REPL_THRESHOLD:-20}
- PHPFPM_THRESHOLD=${PHPFPM_THRESHOLD:-5}
- RATELIMIT_THRESHOLD=${RATELIMIT_THRESHOLD:-1}
- FAIL2BAN_THRESHOLD=${FAIL2BAN_THRESHOLD:-1}
- ACME_THRESHOLD=${ACME_THRESHOLD:-1}
- IPV6NAT_THRESHOLD=${IPV6NAT_THRESHOLD:-1}
- RSPAMD_THRESHOLD=${RSPAMD_THRESHOLD:-5}
- OLEFY_THRESHOLD=${OLEFY_THRESHOLD:-5}
- MAILQ_THRESHOLD=${MAILQ_THRESHOLD:-20}
- MAILQ_CRIT=${MAILQ_CRIT:-30}
networks:
mailcow-network:
aliases:
- watchdog
dockerapi-mailcow:
image: mailcow/dockerapi:1.37
security_opt:
- label=disable
restart: always
oom_kill_disable: true
dns:
- ${IPV4_NETWORK:-172.22.1}.254
environment:
- DBROOT=${DBROOT}
- TZ=${TZ}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
mailcow-network:
aliases:
- dockerapi
solr-mailcow:
image: mailcow/solr:1.7
restart: always
volumes:
- solr-vol-1:/opt/solr/server/solr/dovecot-fts/data:Z
ports:
- "${SOLR_PORT:-127.0.0.1:18983}:8983"
environment:
- TZ=${TZ}
- SOLR_HEAP=${SOLR_HEAP:-1024}
- SKIP_SOLR=${SKIP_SOLR:-y}
networks:
mailcow-network:
aliases:
- solr
olefy-mailcow:
image: mailcow/olefy:1.5
restart: always
environment:
- TZ=${TZ}
- OLEFY_BINDADDRESS=0.0.0.0
- OLEFY_BINDPORT=10055
- OLEFY_TMPDIR=/tmp
- OLEFY_PYTHON_PATH=/usr/bin/python3
- OLEFY_OLEVBA_PATH=/usr/bin/olevba3
- OLEFY_LOGLVL=20
- OLEFY_MINLENGTH=500
- OLEFY_DEL_TMP=1
networks:
mailcow-network:
aliases:
- olefy
ejabberd-mailcow:
image: mailcow/ejabberd:1.2
volumes:
- ./data/conf/ejabberd/ejabberd.yml:/home/ejabberd/conf/ejabberd.yml:z
- xmpp-vol-1:/home/ejabberd/database:z
- xmpp-upload-vol-1:/var/www/upload:z
- ./data/assets/ejabberd/sqlite:/sqlite:z
- ./data/conf/ejabberd/autogen:/ejabberd/:z
- mysql-socket-vol-1:/var/run/mysqld/:z
- ./data/assets/ssl:/ssl:ro,z
restart: always
dns:
- ${IPV4_NETWORK:-172.22.1}.254
hostname: ejabberd.mailcow.local
extra_hosts:
- "${MAILCOW_HOSTNAME}:127.0.0.1"
environment:
- TZ=${TZ}
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- MASTER=${MASTER:-y}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
ports:
- - "${XMPP_C22_PORT:-5222}:5222"
+ - "${XMPP_C2S_PORT:-5222}:5222"
- "${XMPP_S2S_PORT:-5269}:5269"
- "${XMPP_HTTPS_PORT:-5443}:5443"
networks:
mailcow-network:
aliases:
- ejabberd
ipv6nat-mailcow:
depends_on:
- unbound-mailcow
- mysql-mailcow
- redis-mailcow
- clamd-mailcow
- rspamd-mailcow
- php-fpm-mailcow
- sogo-mailcow
- dovecot-mailcow
- postfix-mailcow
- memcached-mailcow
- nginx-mailcow
- acme-mailcow
- netfilter-mailcow
- watchdog-mailcow
- dockerapi-mailcow
- solr-mailcow
environment:
- TZ=${TZ}
image: robbertkl/ipv6nat
security_opt:
- label=disable
restart: always
privileged: true
network_mode: "host"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /lib/modules:/lib/modules:ro
networks:
mailcow-network:
driver: bridge
driver_opts:
com.docker.network.bridge.name: br-mailcow
enable_ipv6: true
ipam:
driver: default
config:
- subnet: ${IPV4_NETWORK:-172.22.1}.0/24
- subnet: ${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64}
volumes:
vmail-vol-1:
vmail-index-vol-1:
mysql-vol-1:
mysql-socket-vol-1:
redis-vol-1:
rspamd-vol-1:
solr-vol-1:
postfix-vol-1:
crypt-vol-1:
sogo-web-vol-1:
sogo-userdata-backup-vol-1:
xmpp-vol-1:
xmpp-upload-vol-1:
diff --git a/generate_config.sh b/generate_config.sh
index 7f5db304..10a3bc66 100755
--- a/generate_config.sh
+++ b/generate_config.sh
@@ -1,336 +1,336 @@
#!/usr/bin/env bash
set -o pipefail
if [[ "$(uname -r)" =~ ^4\.15\.0-60 ]]; then
echo "DO NOT RUN mailcow ON THIS UBUNTU KERNEL!";
echo "Please update to 5.x or use another distribution."
exit 1
fi
if [[ "$(uname -r)" =~ ^4\.4\. ]]; then
if grep -q Ubuntu <<< $(uname -a); then
echo "DO NOT RUN mailcow ON THIS UBUNTU KERNEL!";
echo "Please update to linux-generic-hwe-16.04 by running \"apt-get install --install-recommends linux-generic-hwe-16.04\""
fi
exit 1
fi
if grep --help 2>&1 | grep -q -i "busybox"; then
echo "BusyBox grep detected, please install gnu grep, \"apk add --no-cache --upgrade grep\""
exit 1
fi
if cp --help 2>&1 | grep -q -i "busybox"; then
echo "BusyBox cp detected, please install coreutils, \"apk add --no-cache --upgrade coreutils\""
exit 1
fi
for bin in openssl curl docker-compose docker git awk sha1sum; do
if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
done
if [ -f mailcow.conf ]; then
read -r -p "A config file exists and will be overwritten, are you sure you want to contine? [y/N] " response
case $response in
[yY][eE][sS]|[yY])
mv mailcow.conf mailcow.conf_backup
chmod 600 mailcow.conf_backup
;;
*)
exit 1
;;
esac
fi
echo "Press enter to confirm the detected value '[value]' where applicable or enter a custom value."
while [ -z "${MAILCOW_HOSTNAME}" ]; do
read -p "Mail server hostname (FQDN) - this is not your mail domain, but your mail servers hostname: " -e MAILCOW_HOSTNAME
DOTS=${MAILCOW_HOSTNAME//[^.]};
if [ ${#DOTS} -lt 2 ] && [ ! -z ${MAILCOW_HOSTNAME} ]; then
echo "${MAILCOW_HOSTNAME} is not a FQDN"
MAILCOW_HOSTNAME=
fi
done
if [ -a /etc/timezone ]; then
DETECTED_TZ=$(cat /etc/timezone)
elif [ -a /etc/localtime ]; then
DETECTED_TZ=$(readlink /etc/localtime|sed -n 's|^.*zoneinfo/||p')
fi
while [ -z "${MAILCOW_TZ}" ]; do
if [ -z "${DETECTED_TZ}" ]; then
read -p "Timezone: " -e MAILCOW_TZ
else
read -p "Timezone [${DETECTED_TZ}]: " -e MAILCOW_TZ
[ -z "${MAILCOW_TZ}" ] && MAILCOW_TZ=${DETECTED_TZ}
fi
done
MEM_TOTAL=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
if [ ${MEM_TOTAL} -le "2621440" ]; then
echo "Installed memory is <= 2.5 GiB. It is recommended to disable ClamAV to prevent out-of-memory situations."
echo "ClamAV can be re-enabled by setting SKIP_CLAMD=n in mailcow.conf."
read -r -p "Do you want to disable ClamAV now? [Y/n] " response
case $response in
[nN][oO]|[nN])
SKIP_CLAMD=n
;;
*)
SKIP_CLAMD=y
;;
esac
else
SKIP_CLAMD=n
fi
if [ ${MEM_TOTAL} -le "2097152" ]; then
echo "Disabling Solr on low-memory system."
SKIP_SOLR=y
elif [ ${MEM_TOTAL} -le "3670016" ]; then
echo "Installed memory is <= 3.5 GiB. It is recommended to disable Solr to prevent out-of-memory situations."
echo "Solr is a prone to run OOM and should be monitored. The default Solr heap size is 1024 MiB and should be set in mailcow.conf according to your expected load."
echo "Solr can be re-enabled by setting SKIP_SOLR=n in mailcow.conf but will refuse to start with less than 2 GB total memory."
read -r -p "Do you want to disable Solr now? [Y/n] " response
case $response in
[nN][oO]|[nN])
SKIP_SOLR=n
;;
*)
SKIP_SOLR=y
;;
esac
else
SKIP_SOLR=n
fi
[ ! -f ./data/conf/rspamd/override.d/worker-controller-password.inc ] && echo '# Placeholder' > ./data/conf/rspamd/override.d/worker-controller-password.inc
cat << EOF > mailcow.conf
# ------------------------------
# mailcow web ui configuration
# ------------------------------
# example.org is _not_ a valid hostname, use a fqdn here.
# Default admin user is "admin"
# Default password is "moohoo"
MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
# Password hash algorithm
# Only certain password hash algorithm are supported. For a fully list of supported schemes,
# see https://mailcow.github.io/mailcow-dockerized-docs/model-passwd/
MAILCOW_PASS_SCHEME=BLF-CRYPT
# ------------------------------
# SQL database configuration
# ------------------------------
DBNAME=mailcow
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)
# ------------------------------
# HTTP/S Bindings
# ------------------------------
# You should use HTTPS, but in case of SSL offloaded reverse proxies:
# Might be important: This will also change the binding within the container.
# If you use a proxy within Docker, point it to the ports you set below.
# Do _not_ use IP:PORT in HTTP(S)_BIND or HTTP(S)_PORT
# IMPORTANT: Do not use port 8081, 9081 or 65510!
# Example: HTTP_BIND=1.2.3.4
# For IPv6 see https://mailcow.github.io/mailcow-dockerized-docs/firststeps-ip_bindings/
HTTP_PORT=80
HTTP_BIND=
HTTPS_PORT=443
HTTPS_BIND=
# ------------------------------
# Other bindings
# ------------------------------
# You should leave that alone
# Format: 11.22.33.44:25 or 12.34.56.78:465 etc.
SMTP_PORT=25
SMTPS_PORT=465
SUBMISSION_PORT=587
IMAP_PORT=143
IMAPS_PORT=993
POP_PORT=110
POPS_PORT=995
SIEVE_PORT=4190
DOVEADM_PORT=127.0.0.1:19991
SQL_PORT=127.0.0.1:13306
SOLR_PORT=127.0.0.1:18983
REDIS_PORT=127.0.0.1:7654
-XMPP_C22_PORT=5222
+XMPP_C2S_PORT=5222
XMPP_S2S_PORT=5269
# Your timezone
# See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for a list of timezones
# Use the row named 'TZ database name' + pay attention for 'Notes' row
TZ=${MAILCOW_TZ}
# Fixed project name
# Please use lowercase letters only
COMPOSE_PROJECT_NAME=mailcowdockerized
# Set this to "allow" to enable the anyone pseudo user. Disabled by default.
# When enabled, ACL can be created, that apply to "All authenticated users"
# This should probably only be activated on mail hosts, that are used exclusivly by one organisation.
# Otherwise a user might share data with too many other users.
ACL_ANYONE=disallow
# Garbage collector cleanup
# Deleted domains and mailboxes are moved to /var/vmail/_garbage/timestamp_sanitizedstring
# How long should objects remain in the garbage until they are being deleted? (value in minutes)
# Check interval is hourly
MAILDIR_GC_TIME=7200
# Additional SAN for the certificate
#
# You can use wildcard records to create specific names for every domain you add to mailcow.
# Example: Add domains "example.com" and "example.net" to mailcow, change ADDITIONAL_SAN to a value like:
#ADDITIONAL_SAN=imap.*,smtp.*
# This will expand the certificate to "imap.example.com", "smtp.example.com", "imap.example.net", "imap.example.net"
# plus every domain you add in the future.
#
# You can also just add static names...
#ADDITIONAL_SAN=srv1.example.net
# ...or combine wildcard and static names:
#ADDITIONAL_SAN=imap.*,srv1.example.com
#
ADDITIONAL_SAN=
# Skip running ACME (acme-mailcow, Let's Encrypt certs) - y/n
SKIP_LETS_ENCRYPT=n
# Create seperate certificates for all domains - y/n
# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames
# see https://wiki.dovecot.org/SSL/SNIClientSupport
ENABLE_SSL_SNI=n
# Skip IPv4 check in ACME container - y/n
SKIP_IP_CHECK=n
# Skip HTTP verification in ACME container - y/n
SKIP_HTTP_VERIFICATION=n
# Skip ClamAV (clamd-mailcow) anti-virus (Rspamd will auto-detect a missing ClamAV container) - y/n
SKIP_CLAMD=${SKIP_CLAMD}
# Skip SOGo: Will disable SOGo integration and therefore webmail, DAV protocols and ActiveSync support (experimental, unsupported, not fully implemented) - y/n
SKIP_SOGO=n
# Skip Solr on low-memory systems or if you do not want to store a readable index of your mails in solr-vol-1.
SKIP_SOLR=${SKIP_SOLR}
# Solr heap size in MB, there is no recommendation, please see Solr docs.
# Solr is a prone to run OOM and should be monitored. Unmonitored Solr setups are not recommended.
SOLR_HEAP=1024
# Allow admins to log into SOGo as email user (without any password)
ALLOW_ADMIN_EMAIL_LOGIN=n
# Enable watchdog (watchdog-mailcow) to restart unhealthy containers
USE_WATCHDOG=y
# Send watchdog notifications by mail (sent from watchdog@MAILCOW_HOSTNAME)
# CAUTION:
# 1. You should use external recipients
# 2. Mails are sent unsigned (no DKIM)
# 3. If you use DMARC, create a separate DMARC policy ("v=DMARC1; p=none;" in _dmarc.MAILCOW_HOSTNAME)
# Multiple rcpts allowed, NO quotation marks, NO spaces
#WATCHDOG_NOTIFY_EMAIL=a@example.com,b@example.com,c@example.com
#WATCHDOG_NOTIFY_EMAIL=
# Notify about banned IP (includes whois lookup)
WATCHDOG_NOTIFY_BAN=n
# Checks if mailcow is an open relay. Requires a SAL. More checks will follow.
# https://www.servercow.de/mailcow?lang=en
# https://www.servercow.de/mailcow?lang=de
# No data is collected. Opt-in and anonymous.
# Will only work with unmodified mailcow setups.
WATCHDOG_EXTERNAL_CHECKS=n
# Max log lines per service to keep in Redis logs
LOG_LINES=9999
# Internal IPv4 /24 subnet, format n.n.n (expands to n.n.n.0/24)
# Use private IPv4 addresses only, see https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses
IPV4_NETWORK=172.22.1
# Internal IPv6 subnet in fc00::/7
# Use private IPv6 addresses only, see https://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses
IPV6_NETWORK=fd4d:6169:6c63:6f77::/64
# Use this IPv4 for outgoing connections (SNAT)
#SNAT_TO_SOURCE=
# Use this IPv6 for outgoing connections (SNAT)
#SNAT6_TO_SOURCE=
# Create or override an API key for the web UI
# You _must_ define API_ALLOW_FROM, which is a comma separated list of IPs
# An API key defined as API_KEY has read-write access
# An API key defined as API_KEY_READ_ONLY has read-only access
# Allowed chars for API_KEY and API_KEY_READ_ONLY: a-z, A-Z, 0-9, -
# You can define API_KEY and/or API_KEY_READ_ONLY
#API_KEY=
#API_KEY_READ_ONLY=
#API_ALLOW_FROM=172.22.1.1,127.0.0.1
# mail_home is ~/Maildir
MAILDIR_SUB=Maildir
# SOGo session timeout in minutes
SOGO_EXPIRE_SESSION=480
# DOVECOT_MASTER_USER and DOVECOT_MASTER_PASS must both be provided. No special chars.
# Empty by default to auto-generate master user and password on start.
# User expands to DOVECOT_MASTER_USER@mailcow.local
# LEAVE EMPTY IF UNSURE
DOVECOT_MASTER_USER=
# LEAVE EMPTY IF UNSURE
DOVECOT_MASTER_PASS=
EOF
mkdir -p data/assets/ssl
chmod 600 mailcow.conf
# copy but don't overwrite existing certificate
echo "Generating snake-oil certificate..."
# Making Willich more popular
openssl req -x509 -newkey rsa:4096 -keyout data/assets/ssl-example/key.pem -out data/assets/ssl-example/cert.pem -days 365 -subj "/C=DE/ST=NRW/L=Willich/O=mailcow/OU=mailcow/CN=${MAILCOW_HOSTNAME}" -sha256 -nodes
echo "Copying snake-oil certificate..."
cp -n -d data/assets/ssl-example/*.pem data/assets/ssl/
diff --git a/update.sh b/update.sh
index 05e9b1a5..c8f9eb75 100755
--- a/update.sh
+++ b/update.sh
@@ -1,616 +1,616 @@
#!/usr/bin/env bash
# Check permissions
if [ "$(id -u)" -ne "0" ]; then
echo "You need to be root"
exit 1
fi
if [[ "$(uname -r)" =~ ^4\.15\.0-60 ]]; then
echo "DO NOT RUN mailcow ON THIS UBUNTU KERNEL!";
echo "Please update to 5.x or use another distribution."
exit 1
fi
if [[ "$(uname -r)" =~ ^4\.4\. ]]; then
if grep -q Ubuntu <<< $(uname -a); then
echo "DO NOT RUN mailcow ON THIS UBUNTU KERNEL!"
echo "Please update to linux-generic-hwe-16.04 by running \"apt-get install --install-recommends linux-generic-hwe-16.04\""
exit 1
fi
echo "mailcow on a 4.4.x kernel is not supported. It may or may not work, please upgrade your kernel or continue at your own risk."
read -p "Press any key to continue..." < /dev/tty
fi
# Exit on error and pipefail
set -o pipefail
# Setting high dc timeout
export COMPOSE_HTTP_TIMEOUT=600
# Add /opt/bin to PATH
PATH=$PATH:/opt/bin
umask 0022
for bin in curl docker-compose docker git awk sha1sum; do
if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
done
export LC_ALL=C
DATE=$(date +%Y-%m-%d_%H_%M_%S)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
check_online_status() {
CHECK_ONLINE_IPS=(1.1.1.1 9.9.9.9 8.8.8.8)
for ip in "${CHECK_ONLINE_IPS[@]}"; do
if timeout 3 ping -c 1 ${ip} > /dev/null; then
return 0
fi
done
return 1
}
prefetch_images() {
[[ -z ${BRANCH} ]] && { echo -e "\e[33m\nUnknown branch...\e[0m"; exit 1; }
git fetch origin #${BRANCH}
while read image; do
RET_C=0
until docker pull ${image}; do
RET_C=$((RET_C + 1))
echo -e "\e[33m\nError pulling $image, retrying...\e[0m"
[ ${RET_C} -gt 3 ] && { echo -e "\e[31m\nToo many failed retries, exiting\e[0m"; exit 1; }
sleep 1
done
done < <(git show origin/${BRANCH}:docker-compose.yml | grep "image:" | awk '{ gsub("image:","", $3); print $2 }')
}
docker_garbage() {
IMGS_TO_DELETE=()
for container in $(grep -oP "image: \Kmailcow.+" docker-compose.yml); do
REPOSITORY=${container/:*}
TAG=${container/*:}
V_MAIN=${container/*.}
V_SUB=${container/*.}
EXISTING_TAGS=$(docker images | grep ${REPOSITORY} | awk '{ print $2 }')
for existing_tag in ${EXISTING_TAGS[@]}; do
V_MAIN_EXISTING=${existing_tag/*.}
V_SUB_EXISTING=${existing_tag/*.}
# Not an integer
[[ ! $V_MAIN_EXISTING =~ ^[0-9]+$ ]] && continue
[[ ! $V_SUB_EXISTING =~ ^[0-9]+$ ]] && continue
if [[ $V_MAIN_EXISTING == "latest" ]]; then
echo "Found deprecated label \"latest\" for repository $REPOSITORY, it should be deleted."
IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
elif [[ $V_MAIN_EXISTING -lt $V_MAIN ]]; then
echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted."
IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
elif [[ $V_SUB_EXISTING -lt $V_SUB ]]; then
echo "Found tag $existing_tag for $REPOSITORY, which is older than the current tag $TAG and should be deleted."
IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
fi
done
done
if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then
echo "Run the following command to delete unused image tags:"
echo
echo " docker rmi ${IMGS_TO_DELETE[*]}"
echo
if [ ! $FORCE ]; then
read -r -p "Do you want to delete old image tags right now? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
docker rmi ${IMGS_TO_DELETE[*]}
else
echo "OK, skipped."
fi
else
echo "Skipped image removal because of force mode."
fi
fi
echo -e "\e[32mFurther cleanup...\e[0m"
echo "If you want to cleanup further garbage collected by Docker, please make sure all containers are up and running before cleaning your system by executing \"docker system prune\""
}
while (($#)); do
case "${1}" in
--check|-c)
echo "Checking remote code for updates..."
LATEST_REV=$(git ls-remote --exit-code --refs --quiet https://github.com/mailcow/mailcow-dockerized ${BRANCH} | cut -f1)
if [ $? -ne 0 ]; then
echo "A problem occurred while trying to fetch the latest revision from github."
exit 99
fi
if [[ -z $(git log HEAD --pretty=format:"%H" | grep "${LATEST_REV}") ]]; then
echo "Updated code is available."
git log --date=short --pretty=format:"%ad - %s" $(git rev-parse --short HEAD)..origin/master
exit 0
else
echo "No updates available."
exit 3
fi
;;
--ours)
MERGE_STRATEGY=ours
;;
--skip-start)
SKIP_START=y
;;
--gc)
echo -e "\e[32mCollecting garbage...\e[0m"
docker_garbage
exit 0
;;
--prefetch)
echo -e "\e[32mPrefetching images...\e[0m"
prefetch_images
exit 0
;;
-f|--force)
echo -e "\e[32mForcing Update...\e[0m"
FORCE=y
;;
--no-update-compose)
NO_UPDATE_COMPOSE=y
;;
--help|-h)
echo './update.sh [-c|--check, --ours, --gc, --no-update-compose, --prefetch, --skip-start, -f|--force, -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!
--gc - Run garbage collector to delete old image tags
--no-update-compose - Do not update docker-compose
--prefetch - Only prefetch new images and exit (useful to prepare updates)
--skip-start - Do not start mailcow after update
-f|--force - Force update, do not ask questions
'
exit 1
esac
shift
done
[[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing"; exit 1;}
chmod 600 mailcow.conf
source mailcow.conf
DOTS=${MAILCOW_HOSTNAME//[^.]};
if [ ${#DOTS} -lt 2 ]; then
echo "MAILCOW_HOSTNAME (${MAILCOW_HOSTNAME}) is not a FQDN!"
echo "Please change it to a FQDN and run docker-compose down followed by docker-compose up -d"
exit 1
fi
if grep --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox grep detected, please install gnu grep, \"apk add --no-cache --upgrade grep\""; exit 1; fi
if cp --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox cp detected, please install coreutils, \"apk add --no-cache --upgrade coreutils\""; exit 1; fi
if sed --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox sed detected, please install gnu sed, \"apk add --no-cache --upgrade sed\""; exit 1; fi
CONFIG_ARRAY=(
"SKIP_LETS_ENCRYPT"
"SKIP_SOGO"
"USE_WATCHDOG"
"WATCHDOG_NOTIFY_EMAIL"
"WATCHDOG_NOTIFY_BAN"
"WATCHDOG_EXTERNAL_CHECKS"
"SKIP_CLAMD"
"SKIP_IP_CHECK"
"ADDITIONAL_SAN"
"DOVEADM_PORT"
"IPV4_NETWORK"
"IPV6_NETWORK"
"LOG_LINES"
"SNAT_TO_SOURCE"
"SNAT6_TO_SOURCE"
"COMPOSE_PROJECT_NAME"
"SQL_PORT"
"API_KEY"
"API_KEY_READ_ONLY"
"API_ALLOW_FROM"
"MAILDIR_GC_TIME"
"MAILDIR_SUB"
"ACL_ANYONE"
"SOLR_HEAP"
"SKIP_SOLR"
"ENABLE_SSL_SNI"
"ALLOW_ADMIN_EMAIL_LOGIN"
"SKIP_HTTP_VERIFICATION"
"SOGO_EXPIRE_SESSION"
"REDIS_PORT"
"DOVECOT_MASTER_USER"
"DOVECOT_MASTER_PASS"
"MAILCOW_PASS_SCHEME"
- "XMPP_C22_PORT"
+ "XMPP_C2S_PORT"
"XMPP_S2S_PORT"
)
sed -i --follow-symlinks '$a\' mailcow.conf
for option in ${CONFIG_ARRAY[@]}; do
if [[ ${option} == "ADDITIONAL_SAN" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "${option}=" >> mailcow.conf
fi
elif [[ ${option} == "COMPOSE_PROJECT_NAME" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "COMPOSE_PROJECT_NAME=mailcowdockerized" >> mailcow.conf
fi
elif [[ ${option} == "DOVEADM_PORT" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "DOVEADM_PORT=127.0.0.1:19991" >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_NOTIFY_EMAIL" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "WATCHDOG_NOTIFY_EMAIL=" >> mailcow.conf
fi
elif [[ ${option} == "LOG_LINES" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Max log lines per service to keep in Redis logs' >> mailcow.conf
echo "LOG_LINES=9999" >> mailcow.conf
fi
elif [[ ${option} == "IPV4_NETWORK" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Internal IPv4 /24 subnet, format n.n.n. (expands to n.n.n.0/24)' >> mailcow.conf
echo "IPV4_NETWORK=172.22.1" >> mailcow.conf
fi
elif [[ ${option} == "IPV6_NETWORK" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Internal IPv6 subnet in fc00::/7' >> mailcow.conf
echo "IPV6_NETWORK=fd4d:6169:6c63:6f77::/64" >> mailcow.conf
fi
elif [[ ${option} == "SQL_PORT" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Bind SQL to 127.0.0.1 on port 13306' >> mailcow.conf
echo "SQL_PORT=127.0.0.1:13306" >> mailcow.conf
fi
elif [[ ${option} == "API_KEY" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Create or override API key for web UI' >> mailcow.conf
echo "#API_KEY=" >> mailcow.conf
fi
elif [[ ${option} == "API_KEY_READ_ONLY" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Create or override read-only API key for web UI' >> mailcow.conf
echo "#API_KEY_READ_ONLY=" >> mailcow.conf
fi
elif [[ ${option} == "API_ALLOW_FROM" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Must be set for API_KEY to be active' >> mailcow.conf
echo '# IPs only, no networks (networks can be set via UI)' >> mailcow.conf
echo "#API_ALLOW_FROM=" >> mailcow.conf
fi
elif [[ ${option} == "SNAT_TO_SOURCE" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Use this IPv4 for outgoing connections (SNAT)' >> mailcow.conf
echo "#SNAT_TO_SOURCE=" >> mailcow.conf
fi
elif [[ ${option} == "SNAT6_TO_SOURCE" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Use this IPv6 for outgoing connections (SNAT)' >> mailcow.conf
echo "#SNAT6_TO_SOURCE=" >> mailcow.conf
fi
elif [[ ${option} == "MAILDIR_GC_TIME" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Garbage collector cleanup' >> mailcow.conf
echo '# Deleted domains and mailboxes are moved to /var/vmail/_garbage/timestamp_sanitizedstring' >> mailcow.conf
echo '# How long should objects remain in the garbage until they are being deleted? (value in minutes)' >> mailcow.conf
echo '# Check interval is hourly' >> mailcow.conf
echo 'MAILDIR_GC_TIME=1440' >> mailcow.conf
fi
elif [[ ${option} == "ACL_ANYONE" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Set this to "allow" to enable the anyone pseudo user. Disabled by default.' >> mailcow.conf
echo '# When enabled, ACL can be created, that apply to "All authenticated users"' >> mailcow.conf
echo '# This should probably only be activated on mail hosts, that are used exclusivly by one organisation.' >> mailcow.conf
echo '# Otherwise a user might share data with too many other users.' >> mailcow.conf
echo 'ACL_ANYONE=disallow' >> mailcow.conf
fi
elif [[ ${option} == "SOLR_HEAP" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Solr heap size, there is no recommendation, please see Solr docs.' >> mailcow.conf
echo '# Solr is a prone to run OOM on large systems and should be monitored. Unmonitored Solr setups are not recommended.' >> mailcow.conf
echo '# Solr will refuse to start with total system memory below or equal to 2 GB.' >> mailcow.conf
echo "SOLR_HEAP=1024" >> mailcow.conf
fi
elif [[ ${option} == "SKIP_SOLR" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Solr is disabled by default after upgrading from non-Solr to Solr-enabled mailcows.' >> mailcow.conf
echo '# Disable Solr or if you do not want to store a readable index of your mails in solr-vol-1.' >> mailcow.conf
echo "SKIP_SOLR=y" >> mailcow.conf
fi
elif [[ ${option} == "ENABLE_SSL_SNI" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Create seperate certificates for all domains - y/n' >> mailcow.conf
echo '# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames' >> mailcow.conf
echo '# see https://wiki.dovecot.org/SSL/SNIClientSupport' >> mailcow.conf
echo "ENABLE_SSL_SNI=n" >> mailcow.conf
fi
elif [[ ${option} == "SKIP_SOGO" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Skip SOGo: Will disable SOGo integration and therefore webmail, DAV protocols and ActiveSync support (experimental, unsupported, not fully implemented) - y/n' >> mailcow.conf
echo "SKIP_SOGO=n" >> mailcow.conf
fi
elif [[ ${option} == "MAILDIR_SUB" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# MAILDIR_SUB defines a path in a users virtual home to keep the maildir in. Leave empty for updated setups.' >> mailcow.conf
echo "#MAILDIR_SUB=Maildir" >> mailcow.conf
echo "MAILDIR_SUB=" >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_NOTIFY_BAN" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Notify about banned IP. Includes whois lookup.' >> mailcow.conf
echo "WATCHDOG_NOTIFY_BAN=y" >> mailcow.conf
fi
elif [[ ${option} == "WATCHDOG_EXTERNAL_CHECKS" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Checks if mailcow is an open relay. Requires a SAL. More checks will follow.' >> mailcow.conf
echo '# No data is collected. Opt-in and anonymous.' >> mailcow.conf
echo '# Will only work with unmodified mailcow setups.' >> mailcow.conf
echo "WATCHDOG_EXTERNAL_CHECKS=n" >> mailcow.conf
fi
elif [[ ${option} == "SOGO_EXPIRE_SESSION" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# SOGo session timeout in minutes' >> mailcow.conf
echo "SOGO_EXPIRE_SESSION=480" >> mailcow.conf
fi
elif [[ ${option} == "REDIS_PORT" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "REDIS_PORT=127.0.0.1:7654" >> mailcow.conf
fi
elif [[ ${option} == "DOVECOT_MASTER_USER" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# DOVECOT_MASTER_USER and _PASS must _both_ be provided. No special chars.' >> mailcow.conf
echo '# Empty by default to auto-generate master user and password on start.' >> mailcow.conf
echo '# User expands to DOVECOT_MASTER_USER@mailcow.local' >> mailcow.conf
echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
echo "DOVECOT_MASTER_USER=" >> mailcow.conf
fi
elif [[ ${option} == "DOVECOT_MASTER_PASS" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# LEAVE EMPTY IF UNSURE' >> mailcow.conf
echo "DOVECOT_MASTER_PASS=" >> mailcow.conf
fi
elif [[ ${option} == "MAILCOW_PASS_SCHEME" ]]; then
if ! grep -q ${option} mailcow.conf; then
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/model-passwd/' >> mailcow.conf
echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
fi
- elif [[ ${option} == "XMPP_C22_PORT" ]]; then
+ elif [[ ${option} == "XMPP_C2S_PORT" ]]; then
if ! grep -q ${option} mailcow.conf; then
- echo "XMPP_C22_PORT=5222" >> mailcow.conf
+ echo "XMPP_C2S_PORT=5222" >> mailcow.conf
fi
elif [[ ${option} == "XMPP_S2S_PORT" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "XMPP_S2S_PORT=5269" >> mailcow.conf
fi
elif ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "${option}=n" >> mailcow.conf
fi
done
echo -en "Checking internet connection... "
if ! check_online_status; then
echo -e "\e[31mfailed\e[0m"
exit 1
else
echo -e "\e[32mOK\e[0m"
fi
echo -e "\e[32mChecking for newer update script...\e[0m"
SHA1_1=$(sha1sum update.sh)
git fetch origin #${BRANCH}
git checkout origin/${BRANCH} update.sh
SHA1_2=$(sha1sum update.sh)
if [[ ${SHA1_1} != ${SHA1_2} ]]; then
echo "update.sh changed, please run this script again, exiting."
chmod +x update.sh
exit 2
fi
if [[ -f mailcow.conf ]]; then
source mailcow.conf
else
echo -e "\e[31mNo mailcow.conf - is mailcow installed?\e[0m"
exit 1
fi
if [ ! $FORCE ]; then
read -r -p "Are you sure you want to update mailcow: dockerized? All containers will be stopped. [y/N] " response
if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "OK, exiting."
exit 0
fi
fi
echo -e "\e[32mValidating docker-compose stack configuration...\e[0m"
if ! docker-compose config -q; then
echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
exit 1
fi
echo -e "\e[32mChecking for conflicting bridges...\e[0m"
MAILCOW_BRIDGE=$(docker-compose config | grep -i com.docker.network.bridge.name | cut -d':' -f2)
while read NAT_ID; do
iptables -t nat -D POSTROUTING $NAT_ID
done < <(iptables -L -vn -t nat --line-numbers | grep $IPV4_NETWORK | grep -E 'MASQUERADE.*all' | grep -v ${MAILCOW_BRIDGE} | cut -d' ' -f1)
DIFF_DIRECTORY=update_diffs
DIFF_FILE=${DIFF_DIRECTORY}/diff_before_update_$(date +"%Y-%m-%d-%H-%M-%S")
mv diff_before_update* ${DIFF_DIRECTORY}/ 2> /dev/null
if ! git diff-index --quiet HEAD; then
echo -e "\e[32mSaving diff to ${DIFF_FILE}...\e[0m"
mkdir -p ${DIFF_DIRECTORY}
git diff --stat > ${DIFF_FILE}
git diff >> ${DIFF_FILE}
fi
echo -e "\e[32mPrefetching images...\e[0m"
prefetch_images
echo -e "\e[32mStopping mailcow...\e[0m"
sleep 2
MAILCOW_CONTAINERS=($(docker-compose ps -q))
docker-compose down
echo -e "\e[32mChecking for remaining containers...\e[0m"
sleep 2
for container in "${MAILCOW_CONTAINERS[@]}"; do
docker rm -f "$container" 2> /dev/null
done
# Silently fixing remote url from andryyy to mailcow
git remote set-url origin https://github.com/mailcow/mailcow-dockerized
echo -e "\e[32mCommitting current status...\e[0m"
[[ -z "$(git config user.name)" ]] && git config user.name moo
[[ -z "$(git config user.email)" ]] && git config user.email moo@cow.moo
[[ ! -z $(git ls-files data/conf/rspamd/override.d/worker-controller-password.inc) ]] && git rm data/conf/rspamd/override.d/worker-controller-password.inc
git add -u
git commit -am "Before update on ${DATE}" > /dev/null
echo -e "\e[32mFetching updated code from remote...\e[0m"
git fetch origin #${BRANCH}
echo -e "\e[32mMerging local with remote code (recursive, strategy: \"${MERGE_STRATEGY:-theirs}\", options: \"patience\"...\e[0m"
git config merge.defaultToUpstream true
git merge -X${MERGE_STRATEGY:-theirs} -Xpatience -m "After update on ${DATE}"
# Need to use a variable to not pass return codes of if checks
MERGE_RETURN=$?
if [[ ${MERGE_RETURN} == 128 ]]; then
echo -e "\e[31m\nOh no, what happened?\n=> You most likely added files to your local mailcow instance that were now added to the official mailcow repository. Please move them to another location before updating mailcow.\e[0m"
exit 1
elif [[ ${MERGE_RETURN} == 1 ]]; then
echo -e "\e[93mPotenial conflict, trying to fix...\e[0m"
git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v
git add -A
git commit -m "After update on ${DATE}" > /dev/null
git checkout .
echo -e "\e[32mRemoved and recreated files if necessary.\e[0m"
elif [[ ${MERGE_RETURN} != 0 ]]; then
echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
echo
echo "Run docker-compose up -d to restart your stack without updates or try again after fixing the mentioned errors."
exit 1
fi
if [[ ${NO_UPDATE_COMPOSE} == "y" ]]; then
echo -e "\e[33mNot fetching latest docker-compose, please check for updates manually!\e[0m"
elif [[ -e /etc/alpine-release ]]; then
echo -e "\e[33mNot fetching latest docker-compose, because you are using Alpine Linux without glibc support. Please update docker-compose via apk!\e[0m"
else
echo -e "\e[32mFetching new docker-compose version...\e[0m"
echo -e "\e[32mTrying to determine GLIBC version...\e[0m"
if ldd --version > /dev/null; then
GLIBC_V=$(ldd --version | grep -E '(GLIBC|GNU libc)' | rev | cut -d ' ' -f1 | rev | cut -d '.' -f2)
if [ ! -z "${GLIBC_V}" ] && [ ${GLIBC_V} -gt 27 ]; then
DC_DL_SUFFIX=
else
DC_DL_SUFFIX=legacy
fi
else
DC_DL_SUFFIX=legacy
fi
sleep 1
if [[ ! -z $(which pip) && $(pip list --local 2>&1 | grep -v DEPRECATION | grep -c docker-compose) == 1 ]]; then
true
#prevent breaking a working docker-compose installed with pip
elif [[ $(curl -sL -w "%{http_code}" https://www.servercow.de/docker-compose/latest.php?vers=${DC_DL_SUFFIX} -o /dev/null) == "200" ]]; then
LATEST_COMPOSE=$(curl -#L https://www.servercow.de/docker-compose/latest.php)
COMPOSE_VERSION=$(docker-compose version --short)
if [[ "$LATEST_COMPOSE" != "$COMPOSE_VERSION" ]]; then
COMPOSE_PATH=$(which docker-compose)
if [[ -w ${COMPOSE_PATH} ]]; then
curl -#L https://github.com/docker/compose/releases/download/${LATEST_COMPOSE}/docker-compose-$(uname -s)-$(uname -m) > $COMPOSE_PATH
chmod +x $COMPOSE_PATH
else
echo -e "\e[33mWARNING: $COMPOSE_PATH is not writable, but new version $LATEST_COMPOSE is available (installed: $COMPOSE_VERSION)\e[0m"
fi
fi
else
echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m"
fi
fi
echo -e "\e[32mFetching new images, if any...\e[0m"
sleep 2
docker-compose pull
# Fix missing SSL, does not overwrite existing files
[[ ! -d data/assets/ssl ]] && mkdir -p data/assets/ssl
cp -n -d data/assets/ssl-example/*.pem data/assets/ssl/
echo -e "Checking IPv6 settings... "
if grep -q 'SYSCTL_IPV6_DISABLED=1' mailcow.conf; then
echo
echo '!! IMPORTANT !!'
echo
echo 'SYSCTL_IPV6_DISABLED was removed due to complications. IPv6 can be disabled by editing "docker-compose.yml" and setting "enable_ipv6: true" to "enable_ipv6: false".'
echo 'This setting will only be active after a complete shutdown of mailcow by running "docker-compose down" followed by "docker-compose up -d".'
echo
echo '!! IMPORTANT !!'
echo
read -p "Press any key to continue..." < /dev/tty
fi
# Checking for old project name bug
sed -i --follow-symlinks 's#COMPOSEPROJECT_NAME#COMPOSE_PROJECT_NAME#g' mailcow.conf
# Checking old, wrong bindings
sed -i --follow-symlinks 's/HTTP_BIND=0.0.0.0/HTTP_BIND=/g' mailcow.conf
sed -i --follow-symlinks 's/HTTPS_BIND=0.0.0.0/HTTPS_BIND=/g' mailcow.conf
# Fix Rspamd maps
if [ -f data/conf/rspamd/custom/global_from_blacklist.map ]; then
mv data/conf/rspamd/custom/global_from_blacklist.map data/conf/rspamd/custom/global_smtp_from_blacklist.map
fi
if [ -f data/conf/rspamd/custom/global_from_whitelist.map ]; then
mv data/conf/rspamd/custom/global_from_whitelist.map data/conf/rspamd/custom/global_smtp_from_whitelist.map
fi
# Fix deprecated metrics.conf
if [ -f "data/conf/rspamd/local.d/metrics.conf" ]; then
if [ ! -z "$(git diff --name-only origin/master data/conf/rspamd/local.d/metrics.conf)" ]; then
echo -e "\e[33mWARNING\e[0m - Please migrate your customizations of data/conf/rspamd/local.d/metrics.conf to actions.conf and groups.conf after this update."
echo "The deprecated configuration file metrics.conf will be moved to metrics.conf_deprecated after updating mailcow."
fi
mv data/conf/rspamd/local.d/metrics.conf data/conf/rspamd/local.d/metrics.conf_deprecated
fi
if [[ ${SKIP_START} == "y" ]]; then
echo -e "\e[33mNot starting mailcow, please run \"docker-compose up -d --remove-orphans\" to start mailcow.\e[0m"
else
echo -e "\e[32mStarting mailcow...\e[0m"
sleep 2
docker-compose up -d --remove-orphans
fi
echo -e "\e[32mCollecting garbage...\e[0m"
docker_garbage
#echo "In case you encounter any problem, hard-reset to a state before updating mailcow:"
#echo
#git reflog --color=always | grep "Before update on "
#echo
#echo "Use \"git reset --hard hash-on-the-left\" and run docker-compose up -d afterwards."

File Metadata

Mime Type
text/x-diff
Expires
9月 12 Fri, 2:59 AM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5724
默认替代文本
(74 KB)

Event Timeline