Merge branch 'master' of https://github.com/andryyy/mailcow-dockerized into recipient_map

Conflicts:
	data/web/inc/init_db.inc.php
master
Michael Kuron 2018-01-27 17:22:08 +01:00
commit c30448c4d8
42 changed files with 710 additions and 434 deletions

View File

@ -6,20 +6,38 @@ LABEL maintainer "André Peters <andre.peters@servercow.de>"
COPY dl_files.sh bootstrap.sh ./
# Installation
RUN apk add --update \
&& apk add --no-cache clamav clamav-libunrar curl bash tini \
ENV CLAMAV 0.99.3
RUN apk add --no-cache --virtual build-dependencies alpine-sdk ncurses-dev zlib-dev bzip2-dev pcre-dev linux-headers fts-dev libxml2-dev libressl-dev \
&& apk add --no-cache curl bash tini libxml2 libbz2 pcre fts libressl \
&& wget -O - https://www.clamav.net/downloads/production/clamav-${CLAMAV}.tar.gz | tar xfvz - \
&& cd clamav-${CLAMAV} \
&& LIBS=-lfts ./configure \
--prefix=/usr \
--libdir=/usr/lib \
--sysconfdir=/etc/clamav \
--mandir=/usr/share/man \
--infodir=/usr/share/info \
--without-iconv \
--disable-llvm \
--with-user=clamav \
--with-group=clamav \
--with-dbdir=/var/lib/clamav \
--enable-clamdtop \
--enable-bigstack \
--with-pcre \
&& make -j4 \
&& make install \
&& make clean \
&& cd .. && rm -rf clamav-${CLAMAV} \
&& apk del build-dependencies \
&& addgroup -S clamav \
&& adduser -S -D -h /var/lib/clamav -s /sbin/nologin -G clamav -g clamav clamav \
&& mkdir -p /run/clamav \
&& chown clamav:clamav /run/clamav \
&& chmod +x /dl_files.sh \
&& set -ex; /bin/bash /dl_files.sh \
&& mkdir /run/clamav \
&& chown clamav:clamav /run/clamav \
&& chmod 750 /run/clamav \
&& sed -i '/Foreground yes/s/^#//g' /etc/clamav/clamd.conf \
&& sed -i '/TCPSocket 3310/s/^#//g' /etc/clamav/clamd.conf \
&& sed -i 's#LogFile /var/log/clamav/clamd.log#LogFile /tmp/logpipe_clamd#g' /etc/clamav/clamd.conf \
&& sed -i 's/#PhishingSignatures yes/PhishingSignatures no/g' /etc/clamav/clamd.conf \
&& sed -i 's/#PhishingScanURLs yes/PhishingScanURLs no/g' /etc/clamav/clamd.conf \
&& sed -i 's#UpdateLogFile /var/log/clamav/freshclam.log#UpdateLogFile /tmp/logpipe_freshclam#g' /etc/clamav/freshclam.conf \
&& sed -i '/Foreground yes/s/^#//g' /etc/clamav/freshclam.conf
&& chmod 750 /run/clamav
# Port provision
EXPOSE 3310

View File

@ -33,26 +33,29 @@ open my $file, '<', "/etc/sogo/sieve.creds";
my $creds = <$file>;
close $file;
my ($master_user, $master_pass) = split /:/, $creds;
my $sth = $dbh->prepare("SELECT id, user1, user2, host1, authmech1, password1, exclude, port1, enc1, delete2duplicates, maxage, subfolder2, delete1, delete2 FROM imapsync WHERE active = 1 AND (UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(last_run) > mins_interval * 60 OR last_run IS NULL) ORDER BY last_run");
my $sth = $dbh->prepare("SELECT id, user1, user2, host1, authmech1, password1, exclude, port1, enc1, delete2duplicates, maxage, subfolder2, delete1, delete2, automap, skipcrossduplicates, maxbytespersecond FROM imapsync WHERE active = 1 AND (UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(last_run) > mins_interval * 60 OR last_run IS NULL) ORDER BY last_run");
$sth->execute();
my $row;
while ($row = $sth->fetchrow_arrayref()) {
$id = @$row[0];
$user1 = @$row[1];
$user2 = @$row[2];
$host1 = @$row[3];
$authmech1 = @$row[4];
$password1 = @$row[5];
$exclude = @$row[6];
$port1 = @$row[7];
$enc1 = @$row[8];
$delete2duplicates = @$row[9];
$maxage = @$row[10];
$subfolder2 = @$row[11];
$delete1 = @$row[12];
$delete2 = @$row[13];
$id = @$row[0];
$user1 = @$row[1];
$user2 = @$row[2];
$host1 = @$row[3];
$authmech1 = @$row[4];
$password1 = @$row[5];
$exclude = @$row[6];
$port1 = @$row[7];
$enc1 = @$row[8];
$delete2duplicates = @$row[9];
$maxage = @$row[10];
$subfolder2 = @$row[11];
$delete1 = @$row[12];
$delete2 = @$row[13];
$automap = @$row[14];
$skipcrossduplicates = @$row[15];
$maxbytespersecond = @$row[16];
$is_running = $dbh->prepare("UPDATE imapsync SET is_running = 1 WHERE id = ?");
$is_running->bind_param( 1, ${id} );
@ -72,11 +75,14 @@ while ($row = $sth->fetchrow_arrayref()) {
"--tmpdir", "/tmp",
"--subscribeall",
($exclude eq "" ? () : ("--exclude", $exclude)),
($subfolder2 eq "" ? () : ('--subfolder2', $subfolder2)),
($maxage eq "0" ? () : ('--maxage', $maxage)),
($delete2duplicates ne "1" ? () : ('--delete2duplicates')),
($delete1 ne "1" ? () : ('--delete')),
($delete2 ne "1" ? () : ('--delete2')),
($subfolder2 eq "" ? () : ('--subfolder2', $subfolder2)),
($maxage eq "0" ? () : ('--maxage', $maxage)),
($maxbytespersecond eq "0" ? () : ('--maxbytespersecond', $maxage)),
($delete2duplicates ne "1" ? () : ('--delete2duplicates')),
($delete1 ne "1" ? () : ('--delete')),
($delete2 ne "1" ? () : ('--delete2')),
($automap ne "1" ? () : ('--automap')),
($skipcrossduplicates ne "1" ? () : ('--skipcrossduplicates')),
(!defined($enc1) ? () : ($enc1)),
"--host1", $host1,
"--user1", $user1,

View File

@ -14,11 +14,11 @@ import json
yes_regex = re.compile(r'([yY][eE][sS]|[yY])+$')
if re.search(yes_regex, os.getenv('SKIP_FAIL2BAN', 0)):
print "SKIP_FAIL2BAN=y, Skipping Fail2ban container..."
print 'SKIP_FAIL2BAN=y, Skipping Fail2ban container...'
time.sleep(31536000)
raise SystemExit
r = redis.StrictRedis(host='172.22.1.249', decode_responses=True, port=6379, db=0)
r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0)
pubsub = r.pubsub()
RULES = {}
@ -29,19 +29,23 @@ 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\.:]+)'
r.setnx("F2B_BAN_TIME", "1800")
r.setnx("F2B_MAX_ATTEMPTS", "10")
r.setnx("F2B_RETRY_WINDOW", "600")
r.setnx('F2B_BAN_TIME', '1800')
r.setnx('F2B_MAX_ATTEMPTS', '10')
r.setnx('F2B_RETRY_WINDOW', '600')
r.setnx('F2B_NETBAN_IPV6', '64')
r.setnx('F2B_NETBAN_IPV4', '24')
bans = {}
log = {}
quit_now = False
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")
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')
NETBAN_IPV6 = '/' + str(r.get('F2B_NETBAN_IPV6'))
NETBAN_IPV4 = '/' + str(r.get('F2B_NETBAN_IPV4'))
ip = ipaddress.ip_address(address.decode('ascii'))
if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped:
@ -56,13 +60,13 @@ def ban(address):
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)
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 = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)).decode('ascii'), strict=False)
net = str(net)
if not net in bans or time.time() - bans[net]['last_attempt'] > RETRY_WINDOW:
@ -78,45 +82,45 @@ def ban(address):
if bans[net]['attempts'] >= MAX_ATTEMPTS:
log['time'] = int(round(time.time()))
log['priority'] = "crit"
log['message'] = "Banning %s" % net
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False))
print "Banning %s for %d minutes" % (net, BAN_TIME / 60)
log['priority'] = 'crit'
log['message'] = 'Banning %s' % net
r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
print 'Banning %s for %d minutes' % (net, BAN_TIME / 60)
if type(ip) is ipaddress.IPv4Address:
subprocess.call(["iptables", "-I", "INPUT", "-s", net, "-j", "REJECT"])
subprocess.call(["iptables", "-I", "FORWARD", "-s", net, "-j", "REJECT"])
subprocess.call(['iptables', '-I', 'INPUT', '-s', net, '-j', 'REJECT'])
subprocess.call(['iptables', '-I', 'FORWARD', '-s', net, '-j', 'REJECT'])
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)
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"
log['message'] = "%d more attempts in the next %d seconds until %s is banned" % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net)
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False))
print "%d more attempts in the next %d seconds until %s is banned" % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net)
log['priority'] = 'warn'
log['message'] = '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net)
r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
print '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net)
def unban(net):
log['time'] = int(round(time.time()))
log['priority'] = "info"
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False))
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)
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
log['message'] = 'Unbanning %s' % net
r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
print 'Unbanning %s' % net
if type(ipaddress.ip_network(net.decode('ascii'))) is ipaddress.IPv4Network:
subprocess.call(["iptables", "-D", "INPUT", "-s", net, "-j", "REJECT"])
subprocess.call(["iptables", "-D", "FORWARD", "-s", net, "-j", "REJECT"])
subprocess.call(['iptables', '-D', 'INPUT', '-s', net, '-j', 'REJECT'])
subprocess.call(['iptables', '-D', 'FORWARD', '-s', net, '-j', 'REJECT'])
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)
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):
@ -125,21 +129,21 @@ def quit(signum, frame):
def clear():
log['time'] = int(round(time.time()))
log['priority'] = "info"
log['message'] = "Clearing all bans"
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False))
print "Clearing all bans"
log['priority'] = 'info'
log['message'] = 'Clearing all bans'
r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
print 'Clearing all bans'
for net in bans.copy():
unban(net)
pubsub.unsubscribe()
def watch():
log['time'] = int(round(time.time()))
log['priority'] = "info"
log['message'] = "Watching Redis channel F2B_CHANNEL"
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False))
pubsub.subscribe("F2B_CHANNEL")
print "Subscribing to Redis channel F2B_CHANNEL"
log['priority'] = 'info'
log['message'] = 'Watching Redis channel F2B_CHANNEL'
r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
pubsub.subscribe('F2B_CHANNEL')
print 'Subscribing to Redis channel F2B_CHANNEL'
while True:
for item in pubsub.listen():
for rule_id, rule_regex in RULES.iteritems():
@ -150,18 +154,18 @@ def watch():
ip = ipaddress.ip_address(addr.decode('ascii'))
if ip.is_private or ip.is_loopback:
continue
print "%s matched rule id %d" % (addr, rule_id)
print '%s matched rule id %d' % (addr, rule_id)
log['time'] = int(round(time.time()))
log['priority'] = "warn"
log['message'] = "%s matched rule id %d" % (addr, rule_id)
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False))
log['priority'] = 'warn'
log['message'] = '%s matched rule id %d' % (addr, rule_id)
r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
ban(addr)
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")
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))

View File

@ -1,10 +1,11 @@
FROM php:7.1-fpm-alpine
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
ENV REDIS_PECL 3.1.4
ENV MEMCACHED_PECL 3.0.3
ENV APCU_PECL 5.1.8
ENV REDIS_PECL 3.1.6
ENV MEMCACHED_PECL 3.0.4
ENV APCU_PECL 5.1.9
ENV IMAGICK_PECL 3.4.3
ENV MAILPARSE_PECL 3.0.2
RUN apk add -U --no-cache libxml2-dev \
icu-dev \
@ -41,27 +42,16 @@ RUN apk add -U --no-cache libxml2-dev \
Net_Sieve \
NET_SMTP \
Mail_mime \
&& pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} mailparse \
&& pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} mailparse-${MAILPARSE_PECL} \
&& docker-php-ext-enable redis apcu memcached imagick mailparse \
&& pecl clear-cache \
&& docker-php-ext-configure intl \
&& docker-php-ext-install -j 4 intl gettext ldap sockets soap pdo pdo_mysql xmlrpc gd zip pcntl opcache \
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \
&& docker-php-ext-install -j 4 imap \
&& apk del --purge autoconf g++ make libxml2-dev icu-dev imap-dev openssl-dev cyrus-sasl-dev pcre-dev libpng-dev libpng-dev libjpeg-turbo-dev libwebp-dev zlib-dev imagemagick-dev \
&& { \
echo 'opcache.enable=1'; \
echo 'opcache.enable_cli=1'; \
echo 'opcache.interned_strings_buffer=8'; \
echo 'opcache.max_accelerated_files=10000'; \
echo 'opcache.memory_consumption=128'; \
echo 'opcache.save_comments=1'; \
echo 'opcache.revalidate_freq=1'; \
} > /usr/local/etc/php/conf.d/opcache-recommended.ini
&& apk del --purge autoconf g++ make libxml2-dev icu-dev imap-dev openssl-dev cyrus-sasl-dev pcre-dev libpng-dev libpng-dev libjpeg-turbo-dev libwebp-dev zlib-dev imagemagick-dev
COPY ./docker-entrypoint.sh /
EXPOSE 9000
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["php-fpm"]

View File

@ -26,7 +26,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
syslog-ng-core \
syslog-ng-mod-redis \
&& rm -rf /var/lib/apt/lists/* \
&& touch /etc/default/locale
&& touch /etc/default/locale \
&& printf '#!/bin/bash\n/usr/sbin/postconf -c /opt/postfix/conf "$@"' > /usr/local/sbin/postconf \
&& chmod +x /usr/local/sbin/postconf
RUN addgroup --system --gid 600 zeyple
RUN adduser --system --home /var/lib/zeyple --no-create-home --uid 600 --gid 600 --disabled-login zeyple

View File

@ -6,7 +6,7 @@ while read QUERY; do
echo "500 dunno"
continue
fi
result=$(curl -s http://172.22.1.251:8081/forwardinghosts.php?host=${QUERY[1]})
result=$(curl -s http://nginx:8081/forwardinghosts.php?host=${QUERY[1]})
logger -t whitelist_forwardinghosts -p mail.info "Look up ${QUERY[1]} on whitelist, result $result"
echo ${result}
done

View File

@ -1 +1 @@
settings = "http://172.22.1.251:8081/settings.php";
settings = "http://nginx:8081/settings.php";

View File

@ -18,11 +18,13 @@ done
mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP VIEW IF EXISTS sogo_view"
mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, home, kind, multiple_bookings) AS
SELECT mailbox.username, mailbox.domain, mailbox.username, mailbox.password, mailbox.name, mailbox.username, IFNULL(ga.aliases, ''), IFNULL(gda.ad_alias, ''), CONCAT('/var/vmail/', maildir), mailbox.kind, mailbox.multiple_bookings FROM mailbox
LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username = mailbox.username
CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, sa_aliases, ad_aliases, home, kind, multiple_bookings) AS
SELECT mailbox.username, mailbox.domain, mailbox.username, mailbox.password, mailbox.name, mailbox.username, IFNULL(GROUP_CONCAT(ga.aliases SEPARATOR ' '), ''), IFNULL(gsa.send_as_acl, ''), IFNULL(gda.ad_alias, ''), CONCAT('/var/vmail/', maildir), mailbox.kind, mailbox.multiple_bookings FROM mailbox
LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username REGEXP CONCAT('(^|,)', mailbox.username, '($|,)')
LEFT OUTER JOIN grouped_sender_acl gsa ON gsa.username = mailbox.username
LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username
WHERE mailbox.active = '1';
WHERE mailbox.active = '1'
GROUP BY mailbox.username;
EOF
@ -67,6 +69,7 @@ while read line
<key>MailFieldNames</key>
<array>
<string>aliases</string>
<string>sa_aliases</string>
<string>ad_aliases</string>
</array>
<key>KindFieldName</key>

View File

@ -191,6 +191,7 @@ phpfpm_checks() {
host_ip=$(get_container_ip php-fpm-mailcow)
err_c_cur=${err_count}
cgi-fcgi -bind -connect ${host_ip}:9000 | grep "Content-type" 1>&2; err_count=$(( ${err_count} + ($? * 2)))
cgi-fcgi -bind -connect ${host_ip}:9001 | grep "Content-type" 1>&2; err_count=$(( ${err_count} + ($? * 2)))
/usr/lib/nagios/plugins/check_ping -4 -H ${host_ip} -w 2000,10% -c 4000,100% -p2 1>&2; err_count=$(( ${err_count} + $? ))
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))

View File

@ -25,7 +25,7 @@ server {
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
server_name NC_SERVER_SUB;
server_name NC_SUBD;
root /web/nextcloud/;
@ -38,18 +38,26 @@ server {
location = /.well-known/carddav {
return 301 $client_req_scheme_nc://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $client_req_scheme_nc://$host/remote.php/dav;
}
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /web;
}
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
set_real_ip_from 172.22.1.1;
set_real_ip_from fd00::/8;
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
@ -100,88 +108,3 @@ server {
access_log off;
}
}
server {
include /etc/nginx/conf.d/listen_ssl.active;
include /etc/nginx/mime.types;
charset utf-8;
override_charset on;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
server_name NC_SERVER_SUB;
root /web/nextcloud/;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /.well-known/carddav {
return 301 $client_req_scheme_nc://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $client_req_scheme_nc://$host/remote.php/dav;
}
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
set_real_ip_from 172.22.1.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
location / {
rewrite ^ /index.php$uri;
}
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_pass phpfpm:9000;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}
location ~ ^/(?:updater|ocs-provider)(?:$|/) {
try_files $uri/ =404;
index index.php;
}
location ~ \.(?:css|js|woff|svg|gif)$ {
try_files $uri /index.php$uri$is_args$args;
add_header Cache-Control "public, max-age=15778463";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
access_log off;
}
location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ {
try_files $uri /index.php$uri$is_args$args;
access_log off;
}
}

View File

@ -0,0 +1,47 @@
LogFile /tmp/logpipe_clamd
LogTime yes
LogClean yes
ExtendedDetectionInfo yes
PidFile /run/clamav/clamd.pid
OfficialDatabaseOnly no
LocalSocket /run/clamav/clamd.sock
TCPSocket 3310
StreamMaxLength 25M
MaxThreads 10
ReadTimeout 10
CommandReadTimeout 3
SendBufTimeout 200
MaxQueue 80
IdleTimeout 20
SelfCheck 3600
User clamav
AllowSupplementaryGroups yes
Foreground yes
DetectPUA yes
# See https://github.com/vrtadmin/clamav-faq/blob/master/faq/faq-pua.md
#ExcludePUA NetTool
#ExcludePUA PWTool
#IncludePUA Spy
#IncludePUA Scanner
#IncludePUA RAT
AlgorithmicDetection yes
ScanOLE2 yes
OLE2BlockMacros yes
ScanPDF yes
ScanSWF yes
ScanXMLDOCS yes
ScanHWP3 yes
ScanMail yes
PhishingSignatures no
PhishingScanURLs no
HeuristicScanPrecedence yes
ScanHTML yes
ScanArchive yes
MaxScanSize 50M
MaxFileSize 25M
MaxRecursion 5
MaxFiles 200
ScanOnAccess no
Bytecode yes
BytecodeSecurity TrustSigned
BytecodeTimeout 1000

View File

@ -0,0 +1,17 @@
UpdateLogFile /tmp/logpipe_freshclam
LogTime yes
PidFile /run/clamav/freshclam.pid
DatabaseOwner clamav
AllowSupplementaryGroups yes
DNSDatabaseInfo current.cvd.clamav.net
DatabaseMirror database.clamav.net
MaxAttempts 4
ScriptedUpdates yes
Checks 6
NotifyClamd /etc/clamav/clamd.conf
Foreground yes
ConnectTimeout 20
ReceiveTimeout 20
TestDatabases yes
Bytecode yes

View File

@ -10,7 +10,7 @@ server {
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9000;
fastcgi_pass phpfpm:9001;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

View File

@ -7,6 +7,13 @@ map $http_x_forwarded_proto $client_req_scheme {
https https;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
include /etc/nginx/conf.d/server_name.active;
return 301 https://$host$request_uri;
}
server {
include /etc/nginx/mime.types;
charset utf-8;
@ -17,6 +24,7 @@ server {
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Permitted-Cross-Domain-Policies none;
index index.php index.html;
@ -39,7 +47,10 @@ server {
}
# If behind reverse proxy, forwards the correct IP
set_real_ip_from 172.22.1.1;
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from fd00::/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
@ -65,7 +76,7 @@ server {
}
location /rspamd/ {
proxy_pass http://172.22.1.253:11334/;
proxy_pass http://rspamd:11334/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
@ -97,7 +108,7 @@ server {
}
location ^~ /Microsoft-Server-ActiveSync {
proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync;
proxy_pass http://sogo:20000/SOGo/Microsoft-Server-ActiveSync;
proxy_connect_timeout 1000;
proxy_next_upstream timeout error;
proxy_send_timeout 1000;
@ -119,7 +130,7 @@ server {
}
location ^~ /SOGo {
proxy_pass http://172.22.1.252:20000;
proxy_pass http://sogo:20000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
@ -134,7 +145,7 @@ server {
}
location /SOGo.woa/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_pass http://sogo:9192/WebServerResources/;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
@ -144,7 +155,7 @@ server {
}
location /.woa/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_pass http://sogo:9192/WebServerResources/;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
@ -154,7 +165,7 @@ server {
}
location /SOGo/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_pass http://sogo:9192/WebServerResources/;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
@ -164,7 +175,7 @@ server {
}
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ {
proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2;
proxy_pass http://sogo:9192/$1.SOGo/Resources/$2;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
@ -195,6 +206,7 @@ server {
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Permitted-Cross-Domain-Policies none;
index index.php index.html;
@ -217,7 +229,10 @@ server {
}
# If behind reverse proxy, forwards the correct IP
set_real_ip_from 172.22.1.1;
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from fd00::/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
@ -243,7 +258,7 @@ server {
}
location /rspamd/ {
proxy_pass http://172.22.1.253:11334/;
proxy_pass http://rspamd:11334/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
@ -275,7 +290,7 @@ server {
}
location ^~ /Microsoft-Server-ActiveSync {
proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync;
proxy_pass http://sogo:20000/SOGo/Microsoft-Server-ActiveSync;
proxy_connect_timeout 1000;
proxy_next_upstream timeout error;
proxy_send_timeout 1000;
@ -297,7 +312,7 @@ server {
}
location ^~ /SOGo {
proxy_pass http://172.22.1.252:20000;
proxy_pass http://sogo:20000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
@ -312,7 +327,7 @@ server {
}
location /SOGo.woa/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_pass http://sogo:9192/WebServerResources/;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
@ -322,7 +337,7 @@ server {
}
location /.woa/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_pass http://sogo:9192/WebServerResources/;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
@ -332,7 +347,7 @@ server {
}
location /SOGo/WebServerResources/ {
proxy_pass http://172.22.1.252:9192/WebServerResources/;
proxy_pass http://sogo:9192/WebServerResources/;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;
@ -342,7 +357,7 @@ server {
}
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ {
proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2;
proxy_pass http://sogo:9192/$1.SOGo/Resources/$2;
proxy_set_header Host $http_host;
proxy_cache sogo;
proxy_cache_valid 200 1d;

View File

@ -0,0 +1,7 @@
opcache.enable=1
opcache.enable_cli=1
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.memory_consumption=128
opcache.save_comments=1
opcache.revalidate_freq=1

View File

@ -0,0 +1,12 @@
[system]
user = www-data
group = www-data
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
listen = [::]:9001
access.log = /proc/self/fd/2
clear_env = no
catch_workers_output = yes

View File

@ -0,0 +1,11 @@
[www]
user = www-data
group = www-data
pm = ondemand
pm.max_children = 20
pm.process_idle_timeout = 20s
pm.max_requests = 800
listen = [::]:9000
access.log = /proc/self/fd/2
clear_env = no
catch_workers_output = yes

View File

@ -9,7 +9,7 @@ smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_una
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 [fd4d:6169:6c63:6f77::]/64
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 [fd::]/8
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all

View File

@ -196,7 +196,7 @@ while ($row = array_shift($rows)) {
}
whitelist_header_<?=$username_sane;?> {
<?php
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(CONCAT('^', `value`, '$'), '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(CONCAT('\<', `value`, '\>'), '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
WHERE `object`= :object
AND `option` = 'whitelist_from'");
$stmt->execute(array(':object' => $row['object']));
@ -288,7 +288,7 @@ while ($row = array_shift($rows)) {
}
blacklist_header_<?=$username_sane;?> {
<?php
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(CONCAT('^', `value`, '$'), '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(CONCAT('\<', `value`, '\>'), '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
WHERE `object`= :object
AND `option` = 'blacklist_from'");
$stmt->execute(array(':object' => $row['object']));

View File

@ -1 +1 @@
whitelisted_ip = "http://172.22.1.251:8081/forwardinghosts.php";
whitelisted_ip = "http://nginx:8081/forwardinghosts.php";

View File

@ -4,6 +4,6 @@ secure_ip = "172.16.0.0/12";
secure_ip = "10.0.0.0/8";
secure_ip = "127.0.0.1";
secure_ip = "::1";
secure_ip = "fd4d:6169:6c63:6f77::/64"
secure_ip = "fd00::/8"
.include(try=true; priority=10) "$CONFDIR/override.d/worker-controller-password.inc"
.include(try=true; priority=20) "$CONFDIR/override.d/worker-controller.custom.inc"

View File

@ -5,7 +5,7 @@
PrivateDAndTViewer
);
WOWorkersCount = "20";
WOWorkersCount = "7";
SOGoACLsSendEMailNotifications = YES;
SOGoAppointmentSendEMailNotifications = YES;
SOGoDraftsFolderName = "Drafts";
@ -36,7 +36,7 @@
SOGoMailingMechanism = smtp;
SOGoSMTPAuthenticationType = plain;
SxVMemLimit = 512;
SxVMemLimit = 384;
SOGoMaximumPingInterval = 354;

View File

@ -8,8 +8,11 @@ server:
do-udp: yes
do-tcp: yes
do-daemonize: no
access-control: 172.22.1.0/24 allow
access-control: fd4d:6169:6c63:6f77::/64 allow
access-control: 10.0.0.0/8 allow
access-control: 172.16.0.0/12 allow
access-control: 192.168.0.0/16 allow
access-control: fd00::/8 allow
access-control: fe80::/10 allow
directory: "/etc/unbound"
username: unbound
auto-trust-anchor-file: trusted-key.key
@ -19,7 +22,6 @@ server:
private-address: 169.254.0.0/16
private-address: fd00::/8
private-address: fe80::/10
private-address: fd4d:6169:6c63:6f77::/64
root-hints: "/etc/unbound/root.hints"
hide-identity: yes
hide-version: yes

View File

@ -168,29 +168,31 @@ $tfa_data = get_tfa();
if (!empty($dkim = dkim('details', $domain))) {
?>
<div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" /></div>
<div class="col-xs-2">
<div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" /></div>
<div class="col-md-3">
<p>Domain: <strong><?=htmlspecialchars($domain);?></strong>
<p><span class="label label-success"><?=$lang['admin']['dkim_key_valid'];?></span></p>
<p><span class="label label-primary">Selector '<?=$dkim['dkim_selector'];?>'</span></p>
<p><span class="label label-info"><?=$dkim['length'];?> bit</span></p>
<p class="dkim-label"><span class="label label-success"><?=$lang['admin']['dkim_key_valid'];?></span></p>
<p class="dkim-label"><span class="label label-primary">Selector '<?=$dkim['dkim_selector'];?>'</span></p>
<p class="dkim-label"><span class="label label-info"><?=$dkim['length'];?> bit</span></p>
</p>
</div>
<div class="col-xs-9">
<div class="col-md-8">
<pre><?=$dkim['dkim_txt'];?></pre>
<p data-toggle="modal" data-target="#showDKIMprivKey" id="dkim_priv" style="cursor:pointer;margin-top:-8pt" data-priv-key="<?=$dkim['privkey'];?>"><small> Private key</small></p>
</div>
<hr class="visible-xs visible-sm">
</div>
<?php
}
else {
?>
<div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" disabled /></div>
<div class="col-xs-2">
<div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" disabled /></div>
<div class="col-md-3">
<p>Domain: <strong><?=htmlspecialchars($domain);?></strong><br /><span class="label label-danger"><?=$lang['admin']['dkim_key_missing'];?></span></p>
</div>
<div class="col-xs-9"><pre>-</pre></div>
<div class="col-md-8"><pre>-</pre></div>
<hr class="visible-xs visible-sm">
</div>
<?php
}
@ -198,29 +200,31 @@ $tfa_data = get_tfa();
if (!empty($dkim = dkim('details', $alias_domain))) {
?>
<div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$alias_domain;?>" /></div>
<div class="col-xs-1 col-xs-offset-1">
<div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$alias_domain;?>" /></div>
<div class="col-md-2 col-md-offset-1">
<p><small> Alias-Domain: <strong><?=htmlspecialchars($alias_domain);?></strong></small>
<p><span class="label label-success"><?=$lang['admin']['dkim_key_valid'];?></span></p>
<p><span class="label label-primary">Selector '<?=$dkim['dkim_selector'];?>'</span></p>
<p><span class="label label-info"><?=$dkim['length'];?> bit</span></p>
<p class="dkim-label"><span class="label label-success"><?=$lang['admin']['dkim_key_valid'];?></span></p>
<p class="dkim-label"><span class="label label-primary">Selector '<?=$dkim['dkim_selector'];?>'</span></p>
<p class="dkim-label"><span class="label label-info"><?=$dkim['length'];?> bit</span></p>
</p>
</div>
<div class="col-xs-9">
<div class="col-md-8">
<pre><?=$dkim['dkim_txt'];?></pre>
<p data-toggle="modal" data-target="#showDKIMprivKey" id="dkim_priv" style="cursor:pointer;margin-top:-8pt" data-priv-key="<?=$dkim['privkey'];?>"><small> Private key</small></p>
</div>
<hr class="visible-xs visible-sm">
</div>
<?php
}
else {
?>
<div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" disabled /></div>
<div class="col-xs-1 col-xs-offset-1">
<div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" disabled /></div>
<div class="col-md-2 col-md-offset-1">
<p><small> Alias-Domain: <strong><?=htmlspecialchars($alias_domain);?></strong><br /></small><span class="label label-danger"><?=$lang['admin']['dkim_key_missing'];?></span></p>
</div>
<div class="col-xs-9"><pre>-</pre></div>
<div class="col-md-8"><pre>-</pre></div>
<hr class="visible-xs visible-sm">
</div>
<?php
}
@ -230,18 +234,19 @@ $tfa_data = get_tfa();
if (!empty($dkim = dkim('details', $blind))) {
?>
<div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$blind;?>" /></div>
<div class="col-xs-2">
<div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$blind;?>" /></div>
<div class="col-md-3">
<p>Domain: <strong><?=htmlspecialchars($blind);?></strong>
<p><span class="label label-warning"><?=$lang['admin']['dkim_key_unused'];?></span></p>
<p><span class="label label-primary">Selector '<?=$dkim['dkim_selector'];?>'</span></p>
<p><span class="label label-info"><?=$dkim['length'];?> bit</span></p>
<p class="dkim-label"><span class="label label-warning"><?=$lang['admin']['dkim_key_unused'];?></span></p>
<p class="dkim-label"><span class="label label-primary">Selector '<?=$dkim['dkim_selector'];?>'</span></p>
<p class="dkim-label"><span class="label label-info"><?=$dkim['length'];?> bit</span></p>
</p>
</div>
<div class="col-xs-9">
<div class="col-md-8">
<pre><?=$dkim['dkim_txt'];?></pre>
<p data-toggle="modal" data-target="#showDKIMprivKey" id="dkim_priv" style="cursor:pointer;margin-top:-8pt" data-priv-key="<?=$dkim['privkey'];?>"><small> Private key</small></p>
</div>
<hr class="visible-xs visible-sm">
</div>
<?php
}
@ -346,6 +351,20 @@ $tfa_data = get_tfa();
<label for="retry_window"><?=$lang['admin']['f2b_retry_window'];?>:</label>
<input type="number" class="form-control" id="retry_window" name="retry_window" value="<?=$f2b_data['retry_window'];?>" required>
</div>
<div class="form-group">
<label for="netban_ipv4"><?=$lang['admin']['f2b_netban_ipv4'];?>:</label>
<div class="input-group">
<span class="input-group-addon">/</span>
<input type="number" class="form-control" id="netban_ipv4" name="netban_ipv4" value="<?=$f2b_data['netban_ipv4'];?>" required>
</div>
</div>
<div class="form-group">
<label for="netban_ipv6"><?=$lang['admin']['f2b_netban_ipv6'];?>:</label>
<div class="input-group">
<span class="input-group-addon">/</span>
<input type="number" class="form-control" id="netban_ipv6" name="netban_ipv6" value="<?=$f2b_data['netban_ipv6'];?>" required>
</div>
</div>
<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>
@ -427,7 +446,7 @@ $tfa_data = get_tfa();
?>
</select>
</div>
<button class="btn btn-success" id="edit_selected" data-item="self" data-id="quarantaine" data-api-url='edit/quarantaine' data-api-attr='{"action":"settings"}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
<button class="btn btn-default" id="edit_selected" data-item="self" data-id="quarantaine" data-api-url='edit/quarantaine' data-api-attr='{"action":"settings"}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
</form>
</div>
</div>
@ -441,7 +460,7 @@ $tfa_data = get_tfa();
<form class="form-inline" role="form" method="post" enctype="multipart/form-data">
<p>
<input type="file" name="main_logo" class="filestyle" data-buttonName="btn-default" data-buttonText="Select" accept="image/gif, image/jpeg, image/pjpeg, image/x-png, image/png, image/svg+xml">
<button name="submit_main_logo" type="submit" class="btn btn-success"><span class="glyphicon glyphicon-cloud-upload"></span> <?=$lang['admin']['upload'];?></button>
<button name="submit_main_logo" type="submit" class="btn btn-default"><span class="glyphicon glyphicon-cloud-upload"></span> <?=$lang['admin']['upload'];?></button>
</p>
</form>
<?php
@ -501,7 +520,7 @@ $tfa_data = get_tfa();
?>
</table>
<p><div class="btn-group">
<button class="btn btn-sm btn-success" id="edit_selected" data-item="admin" data-id="app_links" data-reload="no" data-api-url='edit/app_links' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
<button class="btn btn-sm btn-default" id="edit_selected" data-item="admin" data-id="app_links" data-reload="no" data-api-url='edit/app_links' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
<button class="btn btn-sm btn-default" type="button" id="add_app_link_row"><?=$lang['admin']['add_row'];?></button>
</div></p>
</form>
@ -526,7 +545,7 @@ $tfa_data = get_tfa();
<label for="help_text"><?=$lang['admin']['help_text'];?>:</label>
<textarea class="form-control" id="help_text" name="help_text" rows="7"><?=$ui_texts['help_text'];?></textarea>
</div>
<button class="btn btn-success" id="edit_selected" data-item="ui" data-id="uitexts" data-api-url='edit/ui_texts' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
<button class="btn btn-default" id="edit_selected" data-item="ui" data-id="uitexts" data-api-url='edit/ui_texts' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
</form>
</div>
</div>

View File

@ -117,3 +117,22 @@ legend {
.lang-link-disabled {
cursor: not-allowed;
}
.dkim-label {
margin: 0 0 2px !important;
}
.overlay {
background: #fff;
position: absolute;
z-index: 10000;
top: 0; right: 0; bottom: 0; left: 0;
opacity: 0.7;
}
nav .glyphicon {
font-size: 12px !important;
}
.logged-in-as {
border-left: 1px solid #E7E7E7;
}
#top {
padding-top: 70px;
}

View File

@ -704,6 +704,8 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<input type="hidden" value="0" name="delete2duplicates">
<input type="hidden" value="0" name="delete1">
<input type="hidden" value="0" name="delete2">
<input type="hidden" value="0" name="automap">
<input type="hidden" value="0" name="skipcrossduplicates">
<input type="hidden" value="0" name="active">
<div class="form-group">
<label class="control-label col-sm-2" for="host1"><?=$lang['edit']['hostname'];?></label>
@ -743,6 +745,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<label class="control-label col-sm-2" for="mins_interval"><?=$lang['edit']['mins_interval'];?></label>
<div class="col-sm-10">
<input type="number" class="form-control" name="mins_interval" min="1" max="3600" value="<?=htmlspecialchars($result['mins_interval'], ENT_QUOTES, 'UTF-8');?>" required>
<small class="help-block">10-3600</small>
</div>
</div>
<div class="form-group">
@ -754,7 +757,15 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group">
<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
<div class="col-sm-10">
<input type="number" class="form-control" name="maxage" id="maxage" value="<?=htmlspecialchars($result['maxage'], ENT_QUOTES, 'UTF-8');?>">
<input type="number" class="form-control" name="maxage" id="maxage" min="0" max="32000" value="<?=htmlspecialchars($result['maxage'], ENT_QUOTES, 'UTF-8');?>">
<small class="help-block">0-32000</small>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="maxbytespersecond"><?=$lang['edit']['maxbytespersecond'];?></label>
<div class="col-sm-10">
<input type="number" class="form-control" name="maxbytespersecond" id="maxbytespersecond" min="0" max="125000000" value="<?=htmlspecialchars($result['maxbytespersecond'], ENT_QUOTES, 'UTF-8');?>">
<small class="help-block">0-125000000</small>
</div>
</div>
<div class="form-group">
@ -784,6 +795,20 @@ if (isset($_SESSION['mailcow_cc_role'])) {
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label><input type="checkbox" value="1" name="automap" <?=($result['automap']=="1") ? "checked" : "";?>> <?=$lang['edit']['automap'];?></label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label><input type="checkbox" value="1" name="skipcrossduplicates" <?=($result['skipcrossduplicates']=="1") ? "checked" : "";?>> <?=$lang['edit']['skipcrossduplicates'];?></label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">

View File

@ -22,7 +22,9 @@ function setLang(sel) {
$.post( "<?= $_SERVER['REQUEST_URI']; ?>", {lang: sel} );
window.location.href = window.location.pathname + window.location.search;
}
$(window).load(function() {
$(".overlay").hide();
});
$(document).ready(function() {
window.mailcow_alert_box = function(message, type) {
msg = $('<span/>').html(message).text();

View File

@ -12,6 +12,8 @@ function fail2ban($_action, $_data = null) {
$data['ban_time'] = $redis->Get('F2B_BAN_TIME');
$data['max_attempts'] = $redis->Get('F2B_MAX_ATTEMPTS');
$data['retry_window'] = $redis->Get('F2B_RETRY_WINDOW');
$data['netban_ipv4'] = $redis->Get('F2B_NETBAN_IPV4');
$data['netban_ipv6'] = $redis->Get('F2B_NETBAN_IPV6');
$wl = $redis->hGetAll('F2B_WHITELIST');
if (is_array($wl)) {
foreach ($wl as $key => $value) {
@ -50,6 +52,8 @@ function fail2ban($_action, $_data = null) {
$ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
$max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['active_int']);
$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']);
}
else {
$_SESSION['return'] = array(
@ -60,12 +64,20 @@ function fail2ban($_action, $_data = null) {
}
$wl = $_data['whitelist'];
$ban_time = ($ban_time < 60) ? 60 : $ban_time;
$netban_ipv4 = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
$netban_ipv6 = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
$netban_ipv4 = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;
$netban_ipv6 = ($netban_ipv6 > 128) ? 128 : $netban_ipv6;
$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->Set('F2B_NETBAN_IPV4', $netban_ipv4);
$redis->Set('F2B_NETBAN_IPV6', $netban_ipv6);
$redis->Del('F2B_WHITELIST');
if(!empty($wl)) {
$wl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $wl));

View File

@ -459,8 +459,9 @@ function user_get_alias_details($username) {
while ($row = array_shift($run)) {
$data['shared_aliases'] = $row['shared_aliases'];
}
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `direct_aliases` FROM `alias`
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(`address` SEPARATOR ', ') AS `direct_aliases` FROM `alias`
WHERE `goto` = :username_goto
AND `address` NOT LIKE '@%'
AND `address` != :username_address");
$stmt->execute(
array(
@ -477,7 +478,13 @@ function user_get_alias_details($username) {
$stmt->execute(array(':username' => $username));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) {
$data['ad_alias'] = $row['ad_alias'];
if (empty($data['direct_aliases'])) {
$data['direct_aliases'] = $row['ad_alias'];
}
else {
// Probably faster than imploding
$data['direct_aliases'] .= ', ' . $row['ad_alias'];
}
}
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '&#10008;') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` NOT LIKE '@%';");
$stmt->execute(array(':username' => $username));

View File

@ -213,24 +213,30 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
return false;
}
$active = intval($_data['active']);
$delete2duplicates = intval($_data['delete2duplicates']);
$delete1 = intval($_data['delete1']);
$delete2 = intval($_data['delete2']);
$port1 = $_data['port1'];
$host1 = strtolower($_data['host1']);
$password1 = $_data['password1'];
$exclude = $_data['exclude'];
$maxage = $_data['maxage'];
$subfolder2 = $_data['subfolder2'];
$user1 = $_data['user1'];
$mins_interval = $_data['mins_interval'];
$enc1 = $_data['enc1'];
$delete2duplicates = intval($_data['delete2duplicates']);
$delete1 = intval($_data['delete1']);
$delete2 = intval($_data['delete2']);
$skipcrossduplicates = intval($_data['skipcrossduplicates']);
$automap = intval($_data['automap']);
$port1 = $_data['port1'];
$host1 = strtolower($_data['host1']);
$password1 = $_data['password1'];
$exclude = $_data['exclude'];
$maxage = $_data['maxage'];
$maxbytespersecond = $_data['maxbytespersecond'];
$subfolder2 = $_data['subfolder2'];
$user1 = $_data['user1'];
$mins_interval = $_data['mins_interval'];
$enc1 = $_data['enc1'];
if (empty($subfolder2)) {
$subfolder2 = "";
}
if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) {
$maxage = "0";
}
if (!isset($maxbytespersecond) || !filter_var($maxbytespersecond, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 125000000)))) {
$maxbytespersecond = "0";
}
if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) {
$_SESSION['return'] = array(
'type' => 'danger',
@ -287,14 +293,17 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
return false;
}
try {
$stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `active`)
VALUES (:user2, :exclude, :delete1, :delete2, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :active)");
$stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `automap`, `skipcrossduplicates`, `maxbytespersecond`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `active`)
VALUES (:user2, :exclude, :delete1, :delete2, :automap, :skipcrossduplicates, :maxbytespersecond, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :active)");
$stmt->execute(array(
':user2' => $username,
':exclude' => $exclude,
':maxage' => $maxage,
':delete1' => $delete1,
':delete2' => $delete2,
':automap' => $automap,
':skipcrossduplicates' => $skipcrossduplicates,
':maxbytespersecond' => $maxbytespersecond,
':subfolder2' => $subfolder2,
':host1' => $host1,
':authmech1' => 'PLAIN',
@ -1444,6 +1453,8 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
$delete2duplicates = (isset($_data['delete2duplicates'])) ? intval($_data['delete2duplicates']) : $is_now['delete2duplicates'];
$delete1 = (isset($_data['delete1'])) ? intval($_data['delete1']) : $is_now['delete1'];
$delete2 = (isset($_data['delete2'])) ? intval($_data['delete2']) : $is_now['delete2'];
$automap = (isset($_data['automap'])) ? intval($_data['automap']) : $is_now['automap'];
$skipcrossduplicates = (isset($_data['skipcrossduplicates'])) ? intval($_data['skipcrossduplicates']) : $is_now['skipcrossduplicates'];
$port1 = (!empty($_data['port1'])) ? $_data['port1'] : $is_now['port1'];
$password1 = (!empty($_data['password1'])) ? $_data['password1'] : $is_now['password1'];
$host1 = (!empty($_data['host1'])) ? $_data['host1'] : $is_now['host1'];
@ -1452,6 +1463,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
$mins_interval = (!empty($_data['mins_interval'])) ? $_data['mins_interval'] : $is_now['mins_interval'];
$exclude = (!empty($_data['exclude'])) ? $_data['exclude'] : $is_now['exclude'];
$maxage = (isset($_data['maxage']) && $_data['maxage'] != "") ? intval($_data['maxage']) : $is_now['maxage'];
$maxbytespersecond = (isset($_data['maxbytespersecond']) && $_data['maxbytespersecond'] != "") ? intval($_data['maxbytespersecond']) : $is_now['maxbytespersecond'];
}
else {
$_SESSION['return'] = array(
@ -1466,6 +1478,9 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) {
$maxage = "0";
}
if (!isset($maxbytespersecond) || !filter_var($maxbytespersecond, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 125000000)))) {
$maxbytespersecond = "0";
}
if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) {
$_SESSION['return'] = array(
'type' => 'danger',
@ -1502,14 +1517,33 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
return false;
}
try {
$stmt = $pdo->prepare("UPDATE `imapsync` SET `delete1` = :delete1, `delete2` = :delete2, `maxage` = :maxage, `subfolder2` = :subfolder2, `exclude` = :exclude, `host1` = :host1, `last_run` = :last_run, `user1` = :user1, `password1` = :password1, `mins_interval` = :mins_interval, `port1` = :port1, `enc1` = :enc1, `delete2duplicates` = :delete2duplicates, `active` = :active
WHERE `id` = :id");
$stmt = $pdo->prepare("UPDATE `imapsync` SET `delete1` = :delete1,
`delete2` = :delete2,
`automap` = :automap,
`skipcrossduplicates` = :skipcrossduplicates,
`maxage` = :maxage,
`maxbytespersecond` = :maxbytespersecond,
`subfolder2` = :subfolder2,
`exclude` = :exclude,
`host1` = :host1,
`last_run` = :last_run,
`user1` = :user1,
`password1` = :password1,
`mins_interval` = :mins_interval,
`port1` = :port1,
`enc1` = :enc1,
`delete2duplicates` = :delete2duplicates,
`active` = :active
WHERE `id` = :id");
$stmt->execute(array(
':delete1' => $delete1,
':delete2' => $delete2,
':automap' => $automap,
':skipcrossduplicates' => $skipcrossduplicates,
':id' => $id,
':exclude' => $exclude,
':maxage' => $maxage,
':maxbytespersecond' => $maxbytespersecond,
':subfolder2' => $subfolder2,
':host1' => $host1,
':user1' => $user1,

View File

@ -4,6 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Referrer-Policy" content="same-origin">
<title><?=$UI_TEXTS['title_name'];?></title>
<!--[if lt IE 9]>
<script src="/js/html5shiv.min.js"></script>
@ -33,7 +34,8 @@
<link rel="shortcut icon" href="/favicon.png" type="image/png">
<link rel="icon" href="/favicon.png" type="image/png">
</head>
<body style="padding-top: 70px;" id="top">
<body id="top">
<div class="overlay"></div>
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
@ -93,12 +95,12 @@
<?php
if (isset($_SESSION['mailcow_cc_role'])) {
?>
<li<?= (preg_match("/quarantaine/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/quarantaine.php"><span style="font-size: 12px;" class="glyphicon glyphicon-briefcase"></span> <?= $lang['header']['quarantaine']; ?></a></li>
<li<?= (preg_match("/quarantaine/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/quarantaine.php"><span class="glyphicon glyphicon-briefcase"></span> <?= $lang['header']['quarantaine']; ?></a></li>
<?php
}
if ($_SESSION['mailcow_cc_role'] == 'admin') {
?>
<li><a href data-toggle="modal" data-container="sogo-mailcow" data-target="#RestartContainer"><span style="font-size: 12px;" class="glyphicon glyphicon-refresh"></span> <?= $lang['header']['restart_sogo']; ?></a></li>
<li><a href data-toggle="modal" data-container="sogo-mailcow" data-target="#RestartContainer"><span class="glyphicon glyphicon-refresh"></span> <?= $lang['header']['restart_sogo']; ?></a></li>
<?php
}
?>
@ -126,11 +128,11 @@
}
if (!isset($_SESSION['dual-login']) && isset($_SESSION['mailcow_cc_username'])):
?>
<li><a href="#" style="border-left: 1px solid #E7E7E7;" onclick="logout.submit()"><?= sprintf($lang['header']['logged_in_as_logout'], $_SESSION['mailcow_cc_username']); ?></a></li>
<li class="logged-in-as"><a href="#" onclick="logout.submit()"><b><?= $_SESSION['mailcow_cc_username']; ?></b> <span class="glyphicon glyphicon-log-out"></span></a></li>
<?php
elseif (isset($_SESSION['dual-login'])):
?>
<li><a href="#" style="border-left: 1px solid #E7E7E7;" onclick="logout.submit()"><?= sprintf($lang['header']['logged_in_as_logout_dual'], $_SESSION['mailcow_cc_username'], $_SESSION['dual-login']['username']); ?></a></li>
<li class="logged-in-as"><a href="#" onclick="logout.submit()"><b><?= $_SESSION['mailcow_cc_username']; ?> <span class="text-info">(<?= $_SESSION['dual-login']['username']; ?>)</span> </b><span class="glyphicon glyphicon-log-out"></span></a></li>
<?php
endif;
?>

View File

@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
$db_version = "20012021_2202";
$db_version = "27012018_1721";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@ -21,13 +21,14 @@ function init_db_schema() {
AND active = '1'
AND address NOT LIKE '@%'
GROUP BY goto;",
"grouped_sender_acl" => "CREATE VIEW grouped_sender_acl (username, send_as) AS
SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as FROM sender_acl
"grouped_sender_acl" => "CREATE VIEW grouped_sender_acl (username, send_as_acl) AS
SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as_acl FROM sender_acl
WHERE send_as NOT LIKE '@%'
GROUP BY logged_in_as;",
"grouped_domain_alias_address" => "CREATE VIEW grouped_domain_alias_address (username, ad_alias) AS
SELECT username, IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ' '), '') AS ad_alias FROM mailbox
LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username;",
LEFT OUTER JOIN alias_domain ON target_domain=domain
GROUP BY username;",
"sieve_before" => "CREATE VIEW sieve_before (id, username, script_name, script_data) AS
SELECT md5(script_data), username, script_name, script_data FROM sieve_filters
WHERE filter_type = 'prefilter';",
@ -353,12 +354,15 @@ function init_db_schema() {
"password1" => "VARCHAR(255) NOT NULL",
"exclude" => "VARCHAR(500) NOT NULL DEFAULT ''",
"maxage" => "SMALLINT NOT NULL DEFAULT '0'",
"mins_interval" => "VARCHAR(50) NOT NULL",
"mins_interval" => "VARCHAR(50) NOT NULL DEFAULT '0'",
"maxbytespersecond" => "VARCHAR(50) NOT NULL DEFAULT '0'",
"port1" => "SMALLINT NOT NULL",
"enc1" => "ENUM('TLS','SSL','PLAIN') DEFAULT 'TLS'",
"delete2duplicates" => "TINYINT(1) NOT NULL DEFAULT '1'",
"delete1" => "TINYINT(1) NOT NULL DEFAULT '0'",
"delete2" => "TINYINT(1) NOT NULL DEFAULT '0'",
"automap" => "TINYINT(1) NOT NULL DEFAULT '0'",
"skipcrossduplicates" => "TINYINT(1) NOT NULL DEFAULT '0'",
"is_running" => "TINYINT(1) NOT NULL DEFAULT '0'",
"returned_text" => "TEXT",
"last_run" => "TIMESTAMP NULL DEFAULT NULL",

View File

@ -15,7 +15,7 @@ jQuery(function($){
{"name":"sender","title":lang.sender,"breakpoints":"xs sm"},
{"name":"rcpt","title":lang.rcpt, "type": "text"},
{"name":"created","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.received,"style":{"width":"170px"}},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right"},"style":{"width":"205px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right"},"style":{"width":"220px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
],
"rows": $.ajax({
dataType: 'json',

View File

@ -101,6 +101,7 @@ $lang['danger']['spam_alias_max_exceeded'] = 'Maximale Anzahl an Spam-Alias-Adre
$lang['danger']['validity_missing'] = 'Bitte geben Sie eine Gültigkeitsdauer an';
$lang['user']['loading'] = "Lade...";
$lang['user']['active_sieve'] = "Aktiver Filter";
$lang['user']['show_sieve_filters'] = "Zeige aktiven Filter des Benutzers";
$lang['user']['no_active_filter'] = "Kein aktiver Filter vorhanden";
$lang['user']['on'] = 'Ein';
$lang['user']['off'] = 'Aus';
@ -122,7 +123,9 @@ $lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse';
$lang['user']['alias'] = 'Alias';
$lang['user']['aliases'] = 'Aliasse';
$lang['user']['shared_aliases'] = 'Geteilte Alias-Adressen';
$lang['user']['shared_aliases_desc'] = 'Geteilte Alias-Adressen werden nicht bei benutzerdefinierten Einstellungen wie die des Spam-Filters oder der Verschlüsselungsrichtlinie berücksichtigt. Entsprechende Spam-Filter können lediglich von einem Administrator vorgenommen werden.';
$lang['user']['direct_aliases'] = 'Direkte Alias-Adressen';
$lang['user']['direct_aliases_desc'] = 'Nur direkte Alias-Adressen werden für benutzerdefinierte Einstellungen berücksichtigt.';
$lang['user']['domain_aliases'] = 'Domain-Alias Adressen';
$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)';
$lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als Benutzer';
@ -305,6 +308,12 @@ $lang['edit']['encryption'] = 'Verschlüsselung';
$lang['edit']['maxage'] = 'Maximales Alter in Tagen einer Nachricht, die kopiert werden soll</br ><small>(0 = alle Nachrichten kopieren)</small>';
$lang['edit']['subfolder2'] = 'Ziel-Ordner<br><small>(leer = kein Unterordner)</small>';
$lang['edit']['mins_interval'] = 'Intervall (min)';
$lang['edit']['maxbytespersecond'] = 'Max. Übertragungsrate in Bytes/s (0 für unlimitiert)';
$lang['edit']['automap'] = 'Ordner automatisch mappen ("Sent items", "Sent" => "Sent" etc.)';
$lang['edit']['skipcrossduplicates'] = 'Duplikate auch über Ordner hinweg überspringen ("first come, first serve")';
$lang['add']['maxbytespersecond'] = 'Max. Übertragungsrate in Bytes/s (0 für unlimitiert)';
$lang['add']['automap'] = 'Ordner automatisch mappen ("Sent items", "Sent" => "Sent" etc.)';
$lang['add']['skipcrossduplicates'] = 'Duplikate auch über Ordner hinweg überspringen ("first come, first serve")';
$lang['edit']['exclude'] = 'Elemente ausschließen (Regex)';
$lang['edit']['archive'] = 'Archiv-Zugriff';
$lang['edit']['max_mailboxes'] = 'Max. Mailboxanzahl:';
@ -463,6 +472,8 @@ $lang['admin']['f2b_parameters'] = 'Fail2ban Parameter';
$lang['admin']['f2b_ban_time'] = 'Banzeit (s)';
$lang['admin']['f2b_max_attempts'] = 'Max. Versuche';
$lang['admin']['f2b_retry_window'] = 'Wiederholungen im Zeitraum von (s)';
$lang['admin']['f2b_netban_ipv4'] = 'Netzbereich für IPv4 Bans (8-32)';
$lang['admin']['f2b_netban_ipv6'] = 'Netzbereich für IPv6 Bans (8-128)';
$lang['admin']['f2b_whitelist'] = 'Whitelist für Netzwerke und Hosts';
$lang['admin']['restrictions'] = 'Postfix Restriktionen';
$lang['admin']['rr'] = 'Postfix Empfänger Restriktionen';

View File

@ -101,6 +101,7 @@ $lang['danger']['spam_alias_max_exceeded'] = "Max. allowed spam alias addresses
$lang['danger']['validity_missing'] = 'Please assign a period of validity';
$lang['user']['loading'] = "Loading...";
$lang['user']['active_sieve'] = "Active filter";
$lang['user']['show_sieve_filters'] = "Show active user sieve filter";
$lang['user']['no_active_filter'] = "No active filter available";
$lang['user']['on'] = "On";
$lang['user']['off'] = "Off";
@ -122,7 +123,9 @@ $lang['user']['spam_aliases'] = 'Temporary email aliases';
$lang['user']['alias'] = 'Alias';
$lang['user']['aliases'] = 'Aliases';
$lang['user']['shared_aliases'] = 'Shared alias addresses';
$lang['user']['shared_aliases_desc'] = 'A shared alias address is not affected by any user specific settings. A custom spam filter setting can be archived by a domain-wide policy set by an administrator..';
$lang['user']['direct_aliases'] = 'Direct alias addresses';
$lang['user']['direct_aliases_desc'] = 'Direct alias addresses are affected by spam filter and TLS policy settings.';
$lang['user']['domain_aliases'] = 'Domain alias addresses';
$lang['user']['is_catch_all'] = 'Catch-all for domain/s';
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as user';
@ -303,6 +306,12 @@ $lang['edit']['username'] = 'Username';
$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>';
$lang['edit']['maxbytespersecond'] = 'Max. bytes per second (0 equals to unlimited)';
$lang['edit']['automap'] = 'Try to automap folders ("Sent items", "Sent" => "Sent" etc.)';
$lang['edit']['skipcrossduplicates'] = 'Skip duplicate messages across folders (first come, first serve)';
$lang['add']['maxbytespersecond'] = 'Max. bytes per second (0 equals to unlimited)';
$lang['add']['automap'] = 'Try to automap folders ("Sent items", "Sent" => "Sent" etc.)';
$lang['add']['skipcrossduplicates'] = 'Skip duplicate messages across folders (first come, first serve)';
$lang['edit']['subfolder2'] = 'Sync into subfolder on destination<br><small>(empty = do not use subfolder)</small>';
$lang['edit']['mins_interval'] = 'Interval (min)';
$lang['edit']['exclude'] = 'Exclude objects (regex)';
@ -463,6 +472,8 @@ $lang['admin']['f2b_parameters'] = 'Fail2ban parameters';
$lang['admin']['f2b_ban_time'] = 'Ban time (s)';
$lang['admin']['f2b_max_attempts'] = 'Max. attempts';
$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']['search_domain_da'] = 'Search domains';
$lang['admin']['restrictions'] = 'Postfix Restrictions';

View File

@ -391,6 +391,13 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<small class="help-block">0-32000</small>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="maxbytespersecond"><?=$lang['edit']['maxbytespersecond'];?></label>
<div class="col-sm-10">
<input type="number" class="form-control" name="maxbytespersecond" id="maxbytespersecond" min="0" max="125000000" value="0">
<small class="help-block">0-125000000</small>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label>
<div class="col-sm-10">
@ -418,6 +425,20 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label><input type="checkbox" value="1" name="automap"> <?=$lang['add']['automap'];?></label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label><input type="checkbox" value="1" name="skipcrossduplicates"> <?=$lang['add']['skipcrossduplicates'];?></label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">

View File

@ -70,6 +70,13 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<small class="help-block">0-32000</small>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="maxbytespersecond"><?=$lang['edit']['maxbytespersecond'];?></label>
<div class="col-sm-10">
<input type="number" class="form-control" name="maxbytespersecond" id="maxbytespersecond" min="0" max="125000000" value="0">
<small class="help-block">0-125000000</small>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label>
<div class="col-sm-10">
@ -97,6 +104,20 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label><input type="checkbox" value="1" name="automap"> <?=$lang['add']['automap'];?></label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label><input type="checkbox" value="1" name="skipcrossduplicates"> <?=$lang['add']['skipcrossduplicates'];?></label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">

View File

@ -97,7 +97,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<div class="row">
<div class="col-md-3 col-xs-5 text-right"> <span class="glyphicon glyphicon-filter"></span></div>
<div class="col-md-9 col-xs-7">
<p><a href="#userFilterModal" data-toggle="modal">[Show active user sieve filter]</a></p>
<p><a href="#userFilterModal" data-toggle="modal">[<?=$lang['user']['show_sieve_filters'];?>]</a></p>
</div>
</div>
<hr>
@ -105,24 +105,22 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
$user_get_alias_details = user_get_alias_details($username);
?>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['direct_aliases'];?>:</div>
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['direct_aliases'];?>:
<p class="small"><?=$lang['user']['direct_aliases_desc'];?></p>
</div>
<div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['direct_aliases'];?></p>
</div>
</div>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['shared_aliases'];?>:</div>
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['shared_aliases'];?>:
<p class="small"><?=$lang['user']['shared_aliases_desc'];?></p>
</div>
<div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['shared_aliases'];?></p>
</div>
</div>
<hr>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['domain_aliases'];?>:</div>
<div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['ad_alias'];?></p>
</div>
</div>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_also_send_as'];?>:</div>
<div class="col-md-9 col-xs-7">

View File

@ -12,7 +12,7 @@ services:
restart: always
networks:
mailcow-network:
ipv4_address: 172.22.1.254
ipv4_address: ${IPV4_NETWORK}.254
aliases:
- unbound
@ -29,10 +29,9 @@ services:
- MYSQL_PASSWORD=${DBPASS}
restart: always
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
networks:
mailcow-network:
ipv4_address: 172.22.1.250
aliases:
- mysql
@ -44,29 +43,31 @@ services:
environment:
- TZ=${TZ}
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
networks:
mailcow-network:
ipv4_address: 172.22.1.249
ipv4_address: ${IPV4_NETWORK}.249
aliases:
- redis
clamd-mailcow:
image: mailcow/clamd:1.6
image: mailcow/clamd:1.8
build: ./data/Dockerfiles/clamd
restart: always
environment:
- SKIP_CLAMD=${SKIP_CLAMD:-n}
- TZ=${TZ}
- SKIP_CLAMD=${SKIP_CLAMD:-n}
volumes:
- ./data/conf/clamav/:/etc/clamav/
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
networks:
mailcow-network:
aliases:
- clamd
rspamd-mailcow:
image: mailcow/rspamd:1.15
image: mailcow/rspamd:1.16
build: ./data/Dockerfiles/rspamd
stop_grace_period: 30s
depends_on:
@ -82,16 +83,15 @@ services:
- rspamd-vol-1:/var/lib/rspamd
restart: always
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
hostname: rspamd
networks:
mailcow-network:
ipv4_address: 172.22.1.253
aliases:
- rspamd
php-fpm-mailcow:
image: mailcow/phpfpm:1.8
image: mailcow/phpfpm:1.9
build: ./data/Dockerfiles/phpfpm
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on:
@ -101,6 +101,9 @@ services:
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
- dkim-vol-1:/data/dkim
- ./data/conf/rspamd/meta_exporter:/meta_exporter:ro
- ./data/conf/phpfpm/php-fpm.d/www.conf:/usr/local/etc/php-fpm.d/www.conf
- ./data/conf/phpfpm/php-fpm.d/system.conf:/usr/local/etc/php-fpm.d/system.conf
- ./data/conf/phpfpm/php-conf.d/opcache-recommended.ini:/usr/local/etc/php/conf.d/opcache-recommended.ini
environment:
- LOG_LINES=${LOG_LINES}
- TZ=${TZ}
@ -118,14 +121,14 @@ services:
- SMTP_PORT=${SMTP_PORT:-25}
restart: always
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
networks:
mailcow-network:
aliases:
- phpfpm
sogo-mailcow:
image: mailcow/sogo:1.13
image: mailcow/sogo:1.14
build: ./data/Dockerfiles/sogo
environment:
- DBNAME=${DBNAME}
@ -138,15 +141,14 @@ services:
- ./data/conf/sogo/:/etc/sogo/
restart: always
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
networks:
mailcow-network:
ipv4_address: 172.22.1.252
aliases:
- sogo
dovecot-mailcow:
image: mailcow/dovecot:1.17
image: mailcow/dovecot:1.18
build: ./data/Dockerfiles/dovecot
cap_add:
- NET_BIND_SERVICE
@ -176,7 +178,7 @@ services:
soft: 20000
hard: 40000
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
hostname: ${MAILCOW_HOSTNAME}
networks:
mailcow-network:
@ -197,13 +199,15 @@ services:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
cap_add:
- NET_BIND_SERVICE
ports:
- "${SMTP_PORT:-25}:25"
- "${SMTPS_PORT:-465}:465"
- "${SUBMISSION_PORT:-587}:587"
restart: always
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
hostname: ${MAILCOW_HOSTNAME}
networks:
mailcow-network:
@ -214,7 +218,7 @@ services:
image: memcached:alpine
restart: always
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
networks:
mailcow-network:
aliases:
@ -249,10 +253,9 @@ services:
- "${HTTP_BIND:-0.0.0.0}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
restart: always
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
networks:
mailcow-network:
ipv4_address: 172.22.1.251
aliases:
- nginx
@ -263,7 +266,7 @@ services:
image: mailcow/acme:1.28
build: ./data/Dockerfiles/acme
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
environment:
- LOG_LINES=${LOG_LINES}
- ADDITIONAL_SAN=${ADDITIONAL_SAN}
@ -284,7 +287,7 @@ services:
- acme
fail2ban-mailcow:
image: mailcow/fail2ban:1.10
image: mailcow/fail2ban:1.11
build: ./data/Dockerfiles/fail2ban
stop_grace_period: 30s
depends_on:
@ -298,16 +301,17 @@ services:
environment:
- TZ=${TZ}
- SKIP_FAIL2BAN=${SKIP_FAIL2BAN:-n}
- IPV4_NETWORK=${IPV4_NETWORK}
network_mode: "host"
dns:
- 172.22.1.254
- ${IPV4_NETWORK}.254
volumes:
- /lib/modules:/lib/modules:ro
watchdog-mailcow:
image: mailcow/watchdog:1.12
image: mailcow/watchdog:1.13
# Debug
#command: /watchdog.sh
command: /watchdog.sh
build: ./data/Dockerfiles/watchdog
volumes:
- vmail-vol-1:/vmail:ro
@ -323,7 +327,6 @@ services:
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
networks:
mailcow-network:
ipv4_address: 172.22.1.248
aliases:
- watchdog
@ -358,8 +361,8 @@ networks:
ipam:
driver: default
config:
- subnet: 172.22.1.0/24
- subnet: fd4d:6169:6c63:6f77::/64
- subnet: ${IPV4_NETWORK}.0/24
- subnet: ${IPV6_NETWORK}
volumes:
vmail-vol-1:

View File

@ -101,8 +101,16 @@ USE_WATCHDOG=n
# Send notifications by mail (no DKIM signature, sent from watchdog@MAILCOW_HOSTNAME)
#WATCHDOG_NOTIFY_EMAIL=
# 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)
IPV4_NETWORK=172.22.1
# Internal IPv6 subnet in fd00::/8
IPV6_NETWORK=fd4d:6169:6c63:6f77::/64
EOF
mkdir -p data/assets/ssl

View File

@ -79,10 +79,12 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
/web/nextcloud/occ config:system:set redis port --value=6379 --type=integer; \
/web/nextcloud/occ config:system:set memcache.locking --value='\OC\Memcache\Redis' --type=string; \
/web/nextcloud/occ config:system:set memcache.local --value='\OC\Memcache\Redis' --type=string; \
/web/nextcloud/occ config:system:set trusted_proxies 0 --value=fd4d:6169:6c63:6f77::1; \
/web/nextcloud/occ config:system:set trusted_proxies 1 --value=172.22.1.0/24; \
/web/nextcloud/occ config:system:set trusted_domains 1 --value=${MAILCOW_HOSTNAME}; \
/web/nextcloud/occ config:system:set trusted_proxies 0 --value=${IPV6_NETWORK}; \
/web/nextcloud/occ config:system:set trusted_proxies 1 --value=${IPV4_NETWORK}.0/24; \
/web/nextcloud/occ config:system:set overwritewebroot --value=/nextcloud; \
/web/nextcloud/occ config:system:set overwritehost --value=${MAILCOW_HOSTNAME}; \
/web/nextcloud/occ config:system:set overwriteprotocol --value=https; \
/web/nextcloud/occ config:system:set mail_smtpmode --value=smtp; \
/web/nextcloud/occ config:system:set mail_smtpauthtype --value=LOGIN; \
/web/nextcloud/occ config:system:set mail_from_address --value=nextcloud; \
@ -94,10 +96,11 @@ elif [[ ${NC_INSTALL} == "y" ]]; then
/web/nextcloud/occ config:system:set user_backends 0 class --value=OC_User_IMAP"
if [[ ${NC_TYPE} == "subdomain" ]]; then
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:set trusted_domains 1 --value=${NC_SUBD}
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:set overwritewebroot --value=/
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:set overwritehost --value=${NC_SUBD}
cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/
sed -i 's/NC_SUBD/${NC_SUBD}/g' ./data/conf/nginx/nextcloud.conf
sed -i "s/NC_SUBD/${NC_SUBD}/g" ./data/conf/nginx/nextcloud.conf
elif [[ ${NC_TYPE} == "subfolder" ]]; then
cp ./data/assets/nextcloud/site.nextcloud.custom ./data/conf/nginx/
fi

226
update.sh
View File

@ -1,72 +1,91 @@
#!/bin/bash
for bin in curl docker-compose docker git awk sha1sum; do
if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
done
[[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing"; exit 1;}
CONFIG_ARRAY=("SKIP_LETS_ENCRYPT" "USE_WATCHDOG" "WATCHDOG_NOTIFY_EMAIL" "SKIP_CLAMD" "SKIP_IP_CHECK" "SKIP_FAIL2BAN" "ADDITIONAL_SAN" "DOVEADM_PORT")
CONFIG_ARRAY=("SKIP_LETS_ENCRYPT" "USE_WATCHDOG" "WATCHDOG_NOTIFY_EMAIL" "SKIP_CLAMD" "SKIP_IP_CHECK" "SKIP_FAIL2BAN" "ADDITIONAL_SAN" "DOVEADM_PORT" "IPV4_NETWORK" "IPV6_NETWORK" "LOG_LINES")
sed -i '$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=mailcow-dockerized" >> 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
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=mailcow-dockerized" >> 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 ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo "${option}=n" >> 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 fd00::/8' >> mailcow.conf
echo "IPV6_NETWORK=fd4d:6169:6c63:6f77::/64" >> 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... "
curl -o /dev/null google.com -sm3
if [[ $? != 0 ]]; then
echo -e "\e[31mfailed\e[0m"
exit 1
else
echo -e "\e[32mOK\e[0m"
echo -e "\e[31mfailed\e[0m"
exit 1
else
echo -e "\e[32mOK\e[0m"
fi
set -o pipefail
export LC_ALL=C
DATE=$(date +%Y-%m-%d_%H_%M_%S)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
declare -a DC_PARAMS
case "${1}" in
--check|-c)
echo "Checking remote code for updates..."
git fetch origin ${BRANCH}
if ! git diff origin/${BRANCH} --quiet; then
echo "Updated code is available."
exit 0
else
echo "No updates available."
exit 3
fi
;;
esac
while (($#)); do
case "${1}" in
--check|-c)
echo "Checking remote code for updates..."
git fetch origin ${BRANCH}
if [[ $(git branch ${BRANCH} --contains $(git rev-parse origin/${BRANCH}) > /dev/null 2> /dev/null; echo $?) != 0 ]]; then
echo "Updated code is available."
exit 0
else
echo "No updates available."
exit 3
fi
;;
--no-start)
DC_PARAMS=(${DC_PARAMS[@]} "--no-start")
;;
esac
done
echo -e "\e[32mChecking for newer update script...\e[0m"
SHA1_1=$(sha1sum update.sh)
@ -74,22 +93,22 @@ 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 0
echo "update.sh changed, please run this script again, exiting."
chmod +x update.sh
exit 0
fi
if [[ -f mailcow.conf ]]; then
source mailcow.conf
else
echo -e "\e[31mNo mailcow.conf - is mailcow installed?\e[0m"
exit 1
source mailcow.conf
else
echo -e "\e[31mNo mailcow.conf - is mailcow installed?\e[0m"
exit 1
fi
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
echo "OK, exiting."
exit 0
fi
echo -e "Stopping mailcow... "
@ -109,31 +128,31 @@ git merge -Xtheirs -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
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"
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
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
echo -e "\e[32mFetching new docker-compose version...\e[0m"
sleep 2
if [[ $(curl -sL -w "%{http_code}" https://www.servercow.de/docker-compose/latest.php -o /dev/null) == "200" ]]; then
LATEST_COMPOSE=$(curl -#L https://www.servercow.de/docker-compose/latest.php)
curl -#L https://github.com/docker/compose/releases/download/${LATEST_COMPOSE}/docker-compose-$(uname -s)-$(uname -m) > $(which docker-compose)
chmod +x $(which docker-compose)
else
echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m"
LATEST_COMPOSE=$(curl -#L https://www.servercow.de/docker-compose/latest.php)
curl -#L https://github.com/docker/compose/releases/download/${LATEST_COMPOSE}/docker-compose-$(uname -s)-$(uname -m) > $(which docker-compose)
chmod +x $(which docker-compose)
else
echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m"
fi
echo -e "\e[32mFetching new images, if any...\e[0m"
@ -146,48 +165,47 @@ cp -n data/assets/ssl-example/*.pem data/assets/ssl/
echo -e "\e[32mStarting mailcow...\e[0m"
sleep 2
docker-compose up -d --remove-orphans
docker-compose up -d --remove-orphans ${DC_PARAMS[@]}
echo -e "\e[32mCollecting garbage...\e[0m"
IMGS_TO_DELETE=()
for container in $(grep -oP "image: \Kmailcow.+" docker-compose.yml); do
REPOSITORY=${container/:*}
TAG=${container/*:}
V_MAIN=${container/*.}
V_SUB=${container/*.}
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
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
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
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
echo "Run the following command to delete unused image tags:"
echo
echo " docker rmi ${IMGS_TO_DELETE[*]}"
echo
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
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\""