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 ./ COPY dl_files.sh bootstrap.sh ./
# Installation # Installation
RUN apk add --update \ ENV CLAMAV 0.99.3
&& apk add --no-cache clamav clamav-libunrar curl bash tini \
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 \ && chmod +x /dl_files.sh \
&& set -ex; /bin/bash /dl_files.sh \ && set -ex; /bin/bash /dl_files.sh \
&& mkdir /run/clamav \ && chmod 750 /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
# Port provision # Port provision
EXPOSE 3310 EXPOSE 3310

View File

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

View File

@ -14,11 +14,11 @@ import json
yes_regex = re.compile(r'([yY][eE][sS]|[yY])+$') yes_regex = re.compile(r'([yY][eE][sS]|[yY])+$')
if re.search(yes_regex, os.getenv('SKIP_FAIL2BAN', 0)): 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) time.sleep(31536000)
raise SystemExit 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() pubsub = r.pubsub()
RULES = {} 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[5] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
RULES[6] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)' RULES[6] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)'
r.setnx("F2B_BAN_TIME", "1800") r.setnx('F2B_BAN_TIME', '1800')
r.setnx("F2B_MAX_ATTEMPTS", "10") r.setnx('F2B_MAX_ATTEMPTS', '10')
r.setnx("F2B_RETRY_WINDOW", "600") r.setnx('F2B_RETRY_WINDOW', '600')
r.setnx('F2B_NETBAN_IPV6', '64')
r.setnx('F2B_NETBAN_IPV4', '24')
bans = {} bans = {}
log = {} log = {}
quit_now = False quit_now = False
def ban(address): def ban(address):
BAN_TIME = int(r.get("F2B_BAN_TIME")) BAN_TIME = int(r.get('F2B_BAN_TIME'))
MAX_ATTEMPTS = int(r.get("F2B_MAX_ATTEMPTS")) MAX_ATTEMPTS = int(r.get('F2B_MAX_ATTEMPTS'))
RETRY_WINDOW = int(r.get("F2B_RETRY_WINDOW")) RETRY_WINDOW = int(r.get('F2B_RETRY_WINDOW'))
WHITELIST = r.hgetall("F2B_WHITELIST") 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')) ip = ipaddress.ip_address(address.decode('ascii'))
if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped: 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) wl_net = ipaddress.ip_network(wl_key.decode('ascii'), False)
if wl_net.overlaps(self_network): if wl_net.overlaps(self_network):
log['time'] = int(round(time.time())) log['time'] = int(round(time.time()))
log['priority'] = "info" log['priority'] = 'info'
log['message'] = "Address %s is whitelisted by rule %s" % (self_network, wl_net) log['message'] = 'Address %s is whitelisted by rule %s' % (self_network, wl_net)
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
print "Address %s is whitelisted by rule %s" % (self_network, wl_net) print 'Address %s is whitelisted by rule %s' % (self_network, wl_net)
return 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) net = str(net)
if not net in bans or time.time() - bans[net]['last_attempt'] > RETRY_WINDOW: 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: if bans[net]['attempts'] >= MAX_ATTEMPTS:
log['time'] = int(round(time.time())) log['time'] = int(round(time.time()))
log['priority'] = "crit" log['priority'] = 'crit'
log['message'] = "Banning %s" % net log['message'] = 'Banning %s' % net
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
print "Banning %s for %d minutes" % (net, BAN_TIME / 60) print 'Banning %s for %d minutes' % (net, BAN_TIME / 60)
if type(ip) is ipaddress.IPv4Address: if type(ip) is ipaddress.IPv4Address:
subprocess.call(["iptables", "-I", "INPUT", "-s", net, "-j", "REJECT"]) subprocess.call(['iptables', '-I', 'INPUT', '-s', net, '-j', 'REJECT'])
subprocess.call(["iptables", "-I", "FORWARD", "-s", net, "-j", "REJECT"]) subprocess.call(['iptables', '-I', 'FORWARD', '-s', net, '-j', 'REJECT'])
else: else:
subprocess.call(["ip6tables", "-I", "INPUT", "-s", net, "-j", "REJECT"]) subprocess.call(['ip6tables', '-I', 'INPUT', '-s', net, '-j', 'REJECT'])
subprocess.call(["ip6tables", "-I", "FORWARD", "-s", net, "-j", "REJECT"]) subprocess.call(['ip6tables', '-I', 'FORWARD', '-s', net, '-j', 'REJECT'])
r.hset("F2B_ACTIVE_BANS", "%s" % net, log['time'] + BAN_TIME) r.hset('F2B_ACTIVE_BANS', '%s' % net, log['time'] + BAN_TIME)
else: else:
log['time'] = int(round(time.time())) log['time'] = int(round(time.time()))
log['priority'] = "warn" 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) 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)) 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) print '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net)
def unban(net): def unban(net):
log['time'] = int(round(time.time())) log['time'] = int(round(time.time()))
log['priority'] = "info" log['priority'] = 'info'
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
if not net in bans: if not net in bans:
log['message'] = "%s is not banned, skipping unban and deleting from queue (if any)" % 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)) r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
print "%s is not banned, skipping unban and deleting from queue (if any)" % net print '%s is not banned, skipping unban and deleting from queue (if any)' % net
r.hdel("F2B_QUEUE_UNBAN", "%s" % net) r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
return return
log['message'] = "Unbanning %s" % net log['message'] = 'Unbanning %s' % net
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
print "Unbanning %s" % net print 'Unbanning %s' % net
if type(ipaddress.ip_network(net.decode('ascii'))) is ipaddress.IPv4Network: if type(ipaddress.ip_network(net.decode('ascii'))) is ipaddress.IPv4Network:
subprocess.call(["iptables", "-D", "INPUT", "-s", net, "-j", "REJECT"]) subprocess.call(['iptables', '-D', 'INPUT', '-s', net, '-j', 'REJECT'])
subprocess.call(["iptables", "-D", "FORWARD", "-s", net, "-j", "REJECT"]) subprocess.call(['iptables', '-D', 'FORWARD', '-s', net, '-j', 'REJECT'])
else: else:
subprocess.call(["ip6tables", "-D", "INPUT", "-s", net, "-j", "REJECT"]) subprocess.call(['ip6tables', '-D', 'INPUT', '-s', net, '-j', 'REJECT'])
subprocess.call(["ip6tables", "-D", "FORWARD", "-s", net, "-j", "REJECT"]) subprocess.call(['ip6tables', '-D', 'FORWARD', '-s', net, '-j', 'REJECT'])
r.hdel("F2B_ACTIVE_BANS", "%s" % net) r.hdel('F2B_ACTIVE_BANS', '%s' % net)
r.hdel("F2B_QUEUE_UNBAN", "%s" % net) r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
del bans[net] del bans[net]
def quit(signum, frame): def quit(signum, frame):
@ -125,21 +129,21 @@ def quit(signum, frame):
def clear(): def clear():
log['time'] = int(round(time.time())) log['time'] = int(round(time.time()))
log['priority'] = "info" log['priority'] = 'info'
log['message'] = "Clearing all bans" log['message'] = 'Clearing all bans'
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
print "Clearing all bans" print 'Clearing all bans'
for net in bans.copy(): for net in bans.copy():
unban(net) unban(net)
pubsub.unsubscribe() pubsub.unsubscribe()
def watch(): def watch():
log['time'] = int(round(time.time())) log['time'] = int(round(time.time()))
log['priority'] = "info" log['priority'] = 'info'
log['message'] = "Watching Redis channel F2B_CHANNEL" log['message'] = 'Watching Redis channel F2B_CHANNEL'
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
pubsub.subscribe("F2B_CHANNEL") pubsub.subscribe('F2B_CHANNEL')
print "Subscribing to Redis channel F2B_CHANNEL" print 'Subscribing to Redis channel F2B_CHANNEL'
while True: while True:
for item in pubsub.listen(): for item in pubsub.listen():
for rule_id, rule_regex in RULES.iteritems(): for rule_id, rule_regex in RULES.iteritems():
@ -150,18 +154,18 @@ def watch():
ip = ipaddress.ip_address(addr.decode('ascii')) ip = ipaddress.ip_address(addr.decode('ascii'))
if ip.is_private or ip.is_loopback: if ip.is_private or ip.is_loopback:
continue 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['time'] = int(round(time.time()))
log['priority'] = "warn" log['priority'] = 'warn'
log['message'] = "%s matched rule id %d" % (addr, rule_id) log['message'] = '%s matched rule id %d' % (addr, rule_id)
r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False))
ban(addr) ban(addr)
def autopurge(): def autopurge():
while not quit_now: while not quit_now:
BAN_TIME = int(r.get("F2B_BAN_TIME")) BAN_TIME = int(r.get('F2B_BAN_TIME'))
MAX_ATTEMPTS = int(r.get("F2B_MAX_ATTEMPTS")) MAX_ATTEMPTS = int(r.get('F2B_MAX_ATTEMPTS'))
QUEUE_UNBAN = r.hgetall("F2B_QUEUE_UNBAN") QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
if QUEUE_UNBAN: if QUEUE_UNBAN:
for net in QUEUE_UNBAN: for net in QUEUE_UNBAN:
unban(str(net)) unban(str(net))

View File

@ -1,10 +1,11 @@
FROM php:7.1-fpm-alpine FROM php:7.1-fpm-alpine
LABEL maintainer "Andre Peters <andre.peters@servercow.de>" LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
ENV REDIS_PECL 3.1.4 ENV REDIS_PECL 3.1.6
ENV MEMCACHED_PECL 3.0.3 ENV MEMCACHED_PECL 3.0.4
ENV APCU_PECL 5.1.8 ENV APCU_PECL 5.1.9
ENV IMAGICK_PECL 3.4.3 ENV IMAGICK_PECL 3.4.3
ENV MAILPARSE_PECL 3.0.2
RUN apk add -U --no-cache libxml2-dev \ RUN apk add -U --no-cache libxml2-dev \
icu-dev \ icu-dev \
@ -41,27 +42,16 @@ RUN apk add -U --no-cache libxml2-dev \
Net_Sieve \ Net_Sieve \
NET_SMTP \ NET_SMTP \
Mail_mime \ 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 \ && docker-php-ext-enable redis apcu memcached imagick mailparse \
&& pecl clear-cache \ && pecl clear-cache \
&& docker-php-ext-configure intl \ && 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-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-configure imap --with-imap --with-imap-ssl \
&& docker-php-ext-install -j 4 imap \ && 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 \ && 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
COPY ./docker-entrypoint.sh / COPY ./docker-entrypoint.sh /
EXPOSE 9000
ENTRYPOINT ["/docker-entrypoint.sh"] ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["php-fpm"] 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-core \
syslog-ng-mod-redis \ syslog-ng-mod-redis \
&& rm -rf /var/lib/apt/lists/* \ && 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 addgroup --system --gid 600 zeyple
RUN adduser --system --home /var/lib/zeyple --no-create-home --uid 600 --gid 600 --disabled-login 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" echo "500 dunno"
continue continue
fi 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" logger -t whitelist_forwardinghosts -p mail.info "Look up ${QUERY[1]} on whitelist, result $result"
echo ${result} echo ${result}
done 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} -e "DROP VIEW IF EXISTS sogo_view"
mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF 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 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(ga.aliases, ''), IFNULL(gda.ad_alias, ''), CONCAT('/var/vmail/', maildir), mailbox.kind, mailbox.multiple_bookings FROM mailbox 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 = mailbox.username 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 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 EOF
@ -67,6 +69,7 @@ while read line
<key>MailFieldNames</key> <key>MailFieldNames</key>
<array> <array>
<string>aliases</string> <string>aliases</string>
<string>sa_aliases</string>
<string>ad_aliases</string> <string>ad_aliases</string>
</array> </array>
<key>KindFieldName</key> <key>KindFieldName</key>

View File

@ -191,6 +191,7 @@ phpfpm_checks() {
host_ip=$(get_container_ip php-fpm-mailcow) host_ip=$(get_container_ip php-fpm-mailcow)
err_c_cur=${err_count} 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}: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} + $? )) /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} -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} )) [ ${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-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none; add_header X-Permitted-Cross-Domain-Policies none;
server_name NC_SERVER_SUB; server_name NC_SUBD;
root /web/nextcloud/; root /web/nextcloud/;
@ -38,18 +38,26 @@ server {
location = /.well-known/carddav { location = /.well-known/carddav {
return 301 $client_req_scheme_nc://$host/remote.php/dav; return 301 $client_req_scheme_nc://$host/remote.php/dav;
} }
location = /.well-known/caldav { location = /.well-known/caldav {
return 301 $client_req_scheme_nc://$host/remote.php/dav; return 301 $client_req_scheme_nc://$host/remote.php/dav;
} }
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /web;
}
gzip on; gzip on;
gzip_vary on; gzip_vary on;
gzip_comp_level 4; gzip_comp_level 4;
gzip_min_length 256; gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; 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; 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 fd00::/8;
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;
real_ip_header X-Forwarded-For; real_ip_header X-Forwarded-For;
real_ip_recursive on; real_ip_recursive on;
@ -100,88 +108,3 @@ server {
access_log off; 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$ { location ~ \.php$ {
try_files $uri =404; try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9000; fastcgi_pass phpfpm:9001;
fastcgi_index index.php; fastcgi_index index.php;
include fastcgi_params; include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 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; 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 { server {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;
charset utf-8; charset utf-8;
@ -17,6 +24,7 @@ server {
add_header X-XSS-Protection "1; mode=block"; add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none; add_header X-Robots-Tag none;
add_header X-Download-Options noopen; add_header X-Download-Options noopen;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Permitted-Cross-Domain-Policies none; add_header X-Permitted-Cross-Domain-Policies none;
index index.php index.html; index index.php index.html;
@ -39,7 +47,10 @@ server {
} }
# If behind reverse proxy, forwards the correct IP # 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_header X-Forwarded-For;
real_ip_recursive on; real_ip_recursive on;
@ -65,7 +76,7 @@ server {
} }
location /rspamd/ { location /rspamd/ {
proxy_pass http://172.22.1.253:11334/; proxy_pass http://rspamd:11334/;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
@ -97,7 +108,7 @@ server {
} }
location ^~ /Microsoft-Server-ActiveSync { 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_connect_timeout 1000;
proxy_next_upstream timeout error; proxy_next_upstream timeout error;
proxy_send_timeout 1000; proxy_send_timeout 1000;
@ -119,7 +130,7 @@ server {
} }
location ^~ /SOGo { 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-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
@ -134,7 +145,7 @@ server {
} }
location /SOGo.woa/WebServerResources/ { 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_set_header Host $http_host;
proxy_cache sogo; proxy_cache sogo;
proxy_cache_valid 200 1d; proxy_cache_valid 200 1d;
@ -144,7 +155,7 @@ server {
} }
location /.woa/WebServerResources/ { 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_set_header Host $http_host;
proxy_cache sogo; proxy_cache sogo;
proxy_cache_valid 200 1d; proxy_cache_valid 200 1d;
@ -154,7 +165,7 @@ server {
} }
location /SOGo/WebServerResources/ { 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_set_header Host $http_host;
proxy_cache sogo; proxy_cache sogo;
proxy_cache_valid 200 1d; proxy_cache_valid 200 1d;
@ -164,7 +175,7 @@ server {
} }
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ { 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_set_header Host $http_host;
proxy_cache sogo; proxy_cache sogo;
proxy_cache_valid 200 1d; proxy_cache_valid 200 1d;
@ -195,6 +206,7 @@ server {
add_header X-XSS-Protection "1; mode=block"; add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none; add_header X-Robots-Tag none;
add_header X-Download-Options noopen; add_header X-Download-Options noopen;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Permitted-Cross-Domain-Policies none; add_header X-Permitted-Cross-Domain-Policies none;
index index.php index.html; index index.php index.html;
@ -217,7 +229,10 @@ server {
} }
# If behind reverse proxy, forwards the correct IP # 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_header X-Forwarded-For;
real_ip_recursive on; real_ip_recursive on;
@ -243,7 +258,7 @@ server {
} }
location /rspamd/ { location /rspamd/ {
proxy_pass http://172.22.1.253:11334/; proxy_pass http://rspamd:11334/;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
@ -275,7 +290,7 @@ server {
} }
location ^~ /Microsoft-Server-ActiveSync { 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_connect_timeout 1000;
proxy_next_upstream timeout error; proxy_next_upstream timeout error;
proxy_send_timeout 1000; proxy_send_timeout 1000;
@ -297,7 +312,7 @@ server {
} }
location ^~ /SOGo { 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-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
@ -312,7 +327,7 @@ server {
} }
location /SOGo.woa/WebServerResources/ { 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_set_header Host $http_host;
proxy_cache sogo; proxy_cache sogo;
proxy_cache_valid 200 1d; proxy_cache_valid 200 1d;
@ -322,7 +337,7 @@ server {
} }
location /.woa/WebServerResources/ { 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_set_header Host $http_host;
proxy_cache sogo; proxy_cache sogo;
proxy_cache_valid 200 1d; proxy_cache_valid 200 1d;
@ -332,7 +347,7 @@ server {
} }
location /SOGo/WebServerResources/ { 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_set_header Host $http_host;
proxy_cache sogo; proxy_cache sogo;
proxy_cache_valid 200 1d; proxy_cache_valid 200 1d;
@ -342,7 +357,7 @@ server {
} }
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ { 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_set_header Host $http_host;
proxy_cache sogo; proxy_cache sogo;
proxy_cache_valid 200 1d; 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_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases alias_database = hash:/etc/aliases
relayhost = 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 mailbox_size_limit = 0
recipient_delimiter = + recipient_delimiter = +
inet_interfaces = all inet_interfaces = all

View File

@ -196,7 +196,7 @@ while ($row = array_shift($rows)) {
} }
whitelist_header_<?=$username_sane;?> { whitelist_header_<?=$username_sane;?> {
<?php <?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 WHERE `object`= :object
AND `option` = 'whitelist_from'"); AND `option` = 'whitelist_from'");
$stmt->execute(array(':object' => $row['object'])); $stmt->execute(array(':object' => $row['object']));
@ -288,7 +288,7 @@ while ($row = array_shift($rows)) {
} }
blacklist_header_<?=$username_sane;?> { blacklist_header_<?=$username_sane;?> {
<?php <?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 WHERE `object`= :object
AND `option` = 'blacklist_from'"); AND `option` = 'blacklist_from'");
$stmt->execute(array(':object' => $row['object'])); $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 = "10.0.0.0/8";
secure_ip = "127.0.0.1"; secure_ip = "127.0.0.1";
secure_ip = "::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=10) "$CONFDIR/override.d/worker-controller-password.inc"
.include(try=true; priority=20) "$CONFDIR/override.d/worker-controller.custom.inc" .include(try=true; priority=20) "$CONFDIR/override.d/worker-controller.custom.inc"

View File

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

View File

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

View File

@ -168,29 +168,31 @@ $tfa_data = get_tfa();
if (!empty($dkim = dkim('details', $domain))) { if (!empty($dkim = dkim('details', $domain))) {
?> ?>
<div class="row"> <div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" /></div> <div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" /></div>
<div class="col-xs-2"> <div class="col-md-3">
<p>Domain: <strong><?=htmlspecialchars($domain);?></strong> <p>Domain: <strong><?=htmlspecialchars($domain);?></strong>
<p><span class="label label-success"><?=$lang['admin']['dkim_key_valid'];?></span></p> <p class="dkim-label"><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 class="dkim-label"><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-info"><?=$dkim['length'];?> bit</span></p>
</p> </p>
</div> </div>
<div class="col-xs-9"> <div class="col-md-8">
<pre><?=$dkim['dkim_txt'];?></pre> <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> <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> </div>
<hr class="visible-xs visible-sm">
</div> </div>
<?php <?php
} }
else { else {
?> ?>
<div class="row"> <div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" disabled /></div> <div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" disabled /></div>
<div class="col-xs-2"> <div class="col-md-3">
<p>Domain: <strong><?=htmlspecialchars($domain);?></strong><br /><span class="label label-danger"><?=$lang['admin']['dkim_key_missing'];?></span></p> <p>Domain: <strong><?=htmlspecialchars($domain);?></strong><br /><span class="label label-danger"><?=$lang['admin']['dkim_key_missing'];?></span></p>
</div> </div>
<div class="col-xs-9"><pre>-</pre></div> <div class="col-md-8"><pre>-</pre></div>
<hr class="visible-xs visible-sm">
</div> </div>
<?php <?php
} }
@ -198,29 +200,31 @@ $tfa_data = get_tfa();
if (!empty($dkim = dkim('details', $alias_domain))) { if (!empty($dkim = dkim('details', $alias_domain))) {
?> ?>
<div class="row"> <div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$alias_domain;?>" /></div> <div class="col-md-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-2 col-md-offset-1">
<p><small> Alias-Domain: <strong><?=htmlspecialchars($alias_domain);?></strong></small> <p><small> Alias-Domain: <strong><?=htmlspecialchars($alias_domain);?></strong></small>
<p><span class="label label-success"><?=$lang['admin']['dkim_key_valid'];?></span></p> <p class="dkim-label"><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 class="dkim-label"><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-info"><?=$dkim['length'];?> bit</span></p>
</p> </p>
</div> </div>
<div class="col-xs-9"> <div class="col-md-8">
<pre><?=$dkim['dkim_txt'];?></pre> <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> <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> </div>
<hr class="visible-xs visible-sm">
</div> </div>
<?php <?php
} }
else { else {
?> ?>
<div class="row"> <div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" disabled /></div> <div class="col-md-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-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> <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>
<div class="col-xs-9"><pre>-</pre></div> <div class="col-md-8"><pre>-</pre></div>
<hr class="visible-xs visible-sm">
</div> </div>
<?php <?php
} }
@ -230,18 +234,19 @@ $tfa_data = get_tfa();
if (!empty($dkim = dkim('details', $blind))) { if (!empty($dkim = dkim('details', $blind))) {
?> ?>
<div class="row"> <div class="row">
<div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$blind;?>" /></div> <div class="col-md-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$blind;?>" /></div>
<div class="col-xs-2"> <div class="col-md-3">
<p>Domain: <strong><?=htmlspecialchars($blind);?></strong> <p>Domain: <strong><?=htmlspecialchars($blind);?></strong>
<p><span class="label label-warning"><?=$lang['admin']['dkim_key_unused'];?></span></p> <p class="dkim-label"><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 class="dkim-label"><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-info"><?=$dkim['length'];?> bit</span></p>
</p> </p>
</div> </div>
<div class="col-xs-9"> <div class="col-md-8">
<pre><?=$dkim['dkim_txt'];?></pre> <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> <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> </div>
<hr class="visible-xs visible-sm">
</div> </div>
<?php <?php
} }
@ -346,6 +351,20 @@ $tfa_data = get_tfa();
<label for="retry_window"><?=$lang['admin']['f2b_retry_window'];?>:</label> <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> <input type="number" class="form-control" id="retry_window" name="retry_window" value="<?=$f2b_data['retry_window'];?>" required>
</div> </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"> <div class="form-group">
<label for="whitelist"><?=$lang['admin']['f2b_whitelist'];?>:</label> <label for="whitelist"><?=$lang['admin']['f2b_whitelist'];?>:</label>
<textarea class="form-control" id="whitelist" name="whitelist" rows="5"><?=$f2b_data['whitelist'];?></textarea> <textarea class="form-control" id="whitelist" name="whitelist" rows="5"><?=$f2b_data['whitelist'];?></textarea>
@ -427,7 +446,7 @@ $tfa_data = get_tfa();
?> ?>
</select> </select>
</div> </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> </form>
</div> </div>
</div> </div>
@ -441,7 +460,7 @@ $tfa_data = get_tfa();
<form class="form-inline" role="form" method="post" enctype="multipart/form-data"> <form class="form-inline" role="form" method="post" enctype="multipart/form-data">
<p> <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"> <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> </p>
</form> </form>
<?php <?php
@ -501,7 +520,7 @@ $tfa_data = get_tfa();
?> ?>
</table> </table>
<p><div class="btn-group"> <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> <button class="btn btn-sm btn-default" type="button" id="add_app_link_row"><?=$lang['admin']['add_row'];?></button>
</div></p> </div></p>
</form> </form>
@ -526,7 +545,7 @@ $tfa_data = get_tfa();
<label for="help_text"><?=$lang['admin']['help_text'];?>:</label> <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> <textarea class="form-control" id="help_text" name="help_text" rows="7"><?=$ui_texts['help_text'];?></textarea>
</div> </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> </form>
</div> </div>
</div> </div>

View File

@ -117,3 +117,22 @@ legend {
.lang-link-disabled { .lang-link-disabled {
cursor: not-allowed; 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="delete2duplicates">
<input type="hidden" value="0" name="delete1"> <input type="hidden" value="0" name="delete1">
<input type="hidden" value="0" name="delete2"> <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"> <input type="hidden" value="0" name="active">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="host1"><?=$lang['edit']['hostname'];?></label> <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> <label class="control-label col-sm-2" for="mins_interval"><?=$lang['edit']['mins_interval'];?></label>
<div class="col-sm-10"> <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> <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> </div>
<div class="form-group"> <div class="form-group">
@ -754,7 +757,15 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label> <label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
<div class="col-sm-10"> <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> </div>
<div class="form-group"> <div class="form-group">
@ -784,6 +795,20 @@ if (isset($_SESSION['mailcow_cc_role'])) {
</div> </div>
</div> </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="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
<div class="checkbox"> <div class="checkbox">

View File

@ -22,7 +22,9 @@ function setLang(sel) {
$.post( "<?= $_SERVER['REQUEST_URI']; ?>", {lang: sel} ); $.post( "<?= $_SERVER['REQUEST_URI']; ?>", {lang: sel} );
window.location.href = window.location.pathname + window.location.search; window.location.href = window.location.pathname + window.location.search;
} }
$(window).load(function() {
$(".overlay").hide();
});
$(document).ready(function() { $(document).ready(function() {
window.mailcow_alert_box = function(message, type) { window.mailcow_alert_box = function(message, type) {
msg = $('<span/>').html(message).text(); 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['ban_time'] = $redis->Get('F2B_BAN_TIME');
$data['max_attempts'] = $redis->Get('F2B_MAX_ATTEMPTS'); $data['max_attempts'] = $redis->Get('F2B_MAX_ATTEMPTS');
$data['retry_window'] = $redis->Get('F2B_RETRY_WINDOW'); $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'); $wl = $redis->hGetAll('F2B_WHITELIST');
if (is_array($wl)) { if (is_array($wl)) {
foreach ($wl as $key => $value) { 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']); $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']); $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']); $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 { else {
$_SESSION['return'] = array( $_SESSION['return'] = array(
@ -60,12 +64,20 @@ function fail2ban($_action, $_data = null) {
} }
$wl = $_data['whitelist']; $wl = $_data['whitelist'];
$ban_time = ($ban_time < 60) ? 60 : $ban_time; $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; $max_attempts = ($max_attempts < 1) ? 1 : $max_attempts;
$retry_window = ($retry_window < 1) ? 1 : $retry_window; $retry_window = ($retry_window < 1) ? 1 : $retry_window;
try { try {
$redis->Set('F2B_BAN_TIME', $ban_time); $redis->Set('F2B_BAN_TIME', $ban_time);
$redis->Set('F2B_MAX_ATTEMPTS', $max_attempts); $redis->Set('F2B_MAX_ATTEMPTS', $max_attempts);
$redis->Set('F2B_RETRY_WINDOW', $retry_window); $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'); $redis->Del('F2B_WHITELIST');
if(!empty($wl)) { if(!empty($wl)) {
$wl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $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)) { while ($row = array_shift($run)) {
$data['shared_aliases'] = $row['shared_aliases']; $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 WHERE `goto` = :username_goto
AND `address` NOT LIKE '@%'
AND `address` != :username_address"); AND `address` != :username_address");
$stmt->execute( $stmt->execute(
array( array(
@ -477,7 +478,13 @@ function user_get_alias_details($username) {
$stmt->execute(array(':username' => $username)); $stmt->execute(array(':username' => $username));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC); $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) { 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 = $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)); $stmt->execute(array(':username' => $username));

View File

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

View File

@ -4,6 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Referrer-Policy" content="same-origin">
<title><?=$UI_TEXTS['title_name'];?></title> <title><?=$UI_TEXTS['title_name'];?></title>
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="/js/html5shiv.min.js"></script> <script src="/js/html5shiv.min.js"></script>
@ -33,7 +34,8 @@
<link rel="shortcut icon" href="/favicon.png" type="image/png"> <link rel="shortcut icon" href="/favicon.png" type="image/png">
<link rel="icon" href="/favicon.png" type="image/png"> <link rel="icon" href="/favicon.png" type="image/png">
</head> </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"> <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container-fluid"> <div class="container-fluid">
<div class="navbar-header"> <div class="navbar-header">
@ -93,12 +95,12 @@
<?php <?php
if (isset($_SESSION['mailcow_cc_role'])) { 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 <?php
} }
if ($_SESSION['mailcow_cc_role'] == 'admin') { 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 <?php
} }
?> ?>
@ -126,11 +128,11 @@
} }
if (!isset($_SESSION['dual-login']) && isset($_SESSION['mailcow_cc_username'])): 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 <?php
elseif (isset($_SESSION['dual-login'])): 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 <?php
endif; endif;
?> ?>

View File

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

View File

@ -15,7 +15,7 @@ jQuery(function($){
{"name":"sender","title":lang.sender,"breakpoints":"xs sm"}, {"name":"sender","title":lang.sender,"breakpoints":"xs sm"},
{"name":"rcpt","title":lang.rcpt, "type": "text"}, {"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":"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({ "rows": $.ajax({
dataType: 'json', 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['danger']['validity_missing'] = 'Bitte geben Sie eine Gültigkeitsdauer an';
$lang['user']['loading'] = "Lade..."; $lang['user']['loading'] = "Lade...";
$lang['user']['active_sieve'] = "Aktiver Filter"; $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']['no_active_filter'] = "Kein aktiver Filter vorhanden";
$lang['user']['on'] = 'Ein'; $lang['user']['on'] = 'Ein';
$lang['user']['off'] = 'Aus'; $lang['user']['off'] = 'Aus';
@ -122,7 +123,9 @@ $lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse';
$lang['user']['alias'] = 'Alias'; $lang['user']['alias'] = 'Alias';
$lang['user']['aliases'] = 'Aliasse'; $lang['user']['aliases'] = 'Aliasse';
$lang['user']['shared_aliases'] = 'Geteilte Alias-Adressen'; $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'] = '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']['domain_aliases'] = 'Domain-Alias Adressen';
$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)'; $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'; $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']['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']['subfolder2'] = 'Ziel-Ordner<br><small>(leer = kein Unterordner)</small>';
$lang['edit']['mins_interval'] = 'Intervall (min)'; $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']['exclude'] = 'Elemente ausschließen (Regex)';
$lang['edit']['archive'] = 'Archiv-Zugriff'; $lang['edit']['archive'] = 'Archiv-Zugriff';
$lang['edit']['max_mailboxes'] = 'Max. Mailboxanzahl:'; $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_ban_time'] = 'Banzeit (s)';
$lang['admin']['f2b_max_attempts'] = 'Max. Versuche'; $lang['admin']['f2b_max_attempts'] = 'Max. Versuche';
$lang['admin']['f2b_retry_window'] = 'Wiederholungen im Zeitraum von (s)'; $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']['f2b_whitelist'] = 'Whitelist für Netzwerke und Hosts';
$lang['admin']['restrictions'] = 'Postfix Restriktionen'; $lang['admin']['restrictions'] = 'Postfix Restriktionen';
$lang['admin']['rr'] = 'Postfix Empfänger 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['danger']['validity_missing'] = 'Please assign a period of validity';
$lang['user']['loading'] = "Loading..."; $lang['user']['loading'] = "Loading...";
$lang['user']['active_sieve'] = "Active filter"; $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']['no_active_filter'] = "No active filter available";
$lang['user']['on'] = "On"; $lang['user']['on'] = "On";
$lang['user']['off'] = "Off"; $lang['user']['off'] = "Off";
@ -122,7 +123,9 @@ $lang['user']['spam_aliases'] = 'Temporary email aliases';
$lang['user']['alias'] = 'Alias'; $lang['user']['alias'] = 'Alias';
$lang['user']['aliases'] = 'Aliases'; $lang['user']['aliases'] = 'Aliases';
$lang['user']['shared_aliases'] = 'Shared alias addresses'; $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'] = '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']['domain_aliases'] = 'Domain alias addresses';
$lang['user']['is_catch_all'] = 'Catch-all for domain/s'; $lang['user']['is_catch_all'] = 'Catch-all for domain/s';
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as user'; $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']['hostname'] = 'Hostname';
$lang['edit']['encryption'] = 'Encryption'; $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']['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']['subfolder2'] = 'Sync into subfolder on destination<br><small>(empty = do not use subfolder)</small>';
$lang['edit']['mins_interval'] = 'Interval (min)'; $lang['edit']['mins_interval'] = 'Interval (min)';
$lang['edit']['exclude'] = 'Exclude objects (regex)'; $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_ban_time'] = 'Ban time (s)';
$lang['admin']['f2b_max_attempts'] = 'Max. attempts'; $lang['admin']['f2b_max_attempts'] = 'Max. attempts';
$lang['admin']['f2b_retry_window'] = 'Retry window (s) for 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']['f2b_whitelist'] = 'Whitelisted networks/hosts';
$lang['admin']['search_domain_da'] = 'Search domains'; $lang['admin']['search_domain_da'] = 'Search domains';
$lang['admin']['restrictions'] = 'Postfix Restrictions'; $lang['admin']['restrictions'] = 'Postfix Restrictions';

View File

@ -391,6 +391,13 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<small class="help-block">0-32000</small> <small class="help-block">0-32000</small>
</div> </div>
</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"> <div class="form-group">
<label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label> <label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -418,6 +425,20 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
</div> </div>
</div> </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="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
<div class="checkbox"> <div class="checkbox">

View File

@ -70,6 +70,13 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<small class="help-block">0-32000</small> <small class="help-block">0-32000</small>
</div> </div>
</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"> <div class="form-group">
<label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label> <label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -97,6 +104,20 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
</div> </div>
</div> </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="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
<div class="checkbox"> <div class="checkbox">

View File

@ -97,7 +97,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<div class="row"> <div class="row">
<div class="col-md-3 col-xs-5 text-right"> <span class="glyphicon glyphicon-filter"></span></div> <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"> <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>
</div> </div>
<hr> <hr>
@ -105,24 +105,22 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
$user_get_alias_details = user_get_alias_details($username); $user_get_alias_details = user_get_alias_details($username);
?> ?>
<div class="row"> <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"> <div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['direct_aliases'];?></p> <p><?=$user_get_alias_details['direct_aliases'];?></p>
</div> </div>
</div> </div>
<div class="row"> <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"> <div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['shared_aliases'];?></p> <p><?=$user_get_alias_details['shared_aliases'];?></p>
</div> </div>
</div> </div>
<hr> <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="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_also_send_as'];?>:</div> <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"> <div class="col-md-9 col-xs-7">

View File

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

View File

@ -101,8 +101,16 @@ USE_WATCHDOG=n
# Send notifications by mail (no DKIM signature, sent from watchdog@MAILCOW_HOSTNAME) # Send notifications by mail (no DKIM signature, sent from watchdog@MAILCOW_HOSTNAME)
#WATCHDOG_NOTIFY_EMAIL= #WATCHDOG_NOTIFY_EMAIL=
# Max log lines per service to keep in Redis logs
LOG_LINES=9999 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 EOF
mkdir -p data/assets/ssl 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 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.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 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_domains 1 --value=${MAILCOW_HOSTNAME}; \
/web/nextcloud/occ config:system:set trusted_proxies 1 --value=172.22.1.0/24; \ /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 overwritewebroot --value=/nextcloud; \
/web/nextcloud/occ config:system:set overwritehost --value=${MAILCOW_HOSTNAME}; \ /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_smtpmode --value=smtp; \
/web/nextcloud/occ config:system:set mail_smtpauthtype --value=LOGIN; \ /web/nextcloud/occ config:system:set mail_smtpauthtype --value=LOGIN; \
/web/nextcloud/occ config:system:set mail_from_address --value=nextcloud; \ /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" /web/nextcloud/occ config:system:set user_backends 0 class --value=OC_User_IMAP"
if [[ ${NC_TYPE} == "subdomain" ]]; then 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 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} 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/ 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 elif [[ ${NC_TYPE} == "subfolder" ]]; then
cp ./data/assets/nextcloud/site.nextcloud.custom ./data/conf/nginx/ cp ./data/assets/nextcloud/site.nextcloud.custom ./data/conf/nginx/
fi fi

226
update.sh
View File

@ -1,72 +1,91 @@
#!/bin/bash #!/bin/bash
for bin in curl docker-compose docker git awk sha1sum; do 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 done
[[ ! -f mailcow.conf ]] && { echo "mailcow.conf is missing"; exit 1;} [[ ! -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 sed -i '$a\' mailcow.conf
for option in ${CONFIG_ARRAY[@]}; do for option in ${CONFIG_ARRAY[@]}; do
if [[ ${option} == "ADDITIONAL_SAN" ]]; then if [[ ${option} == "ADDITIONAL_SAN" ]]; then
if ! grep -q ${option} mailcow.conf; then if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf" echo "Adding new option \"${option}\" to mailcow.conf"
echo "${option}=" >> mailcow.conf echo "${option}=" >> mailcow.conf
fi fi
elif [[ ${option} == "COMPOSE_PROJECT_NAME" ]]; then elif [[ ${option} == "COMPOSE_PROJECT_NAME" ]]; then
if ! grep -q ${option} mailcow.conf; then if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf" echo "Adding new option \"${option}\" to mailcow.conf"
echo "COMPOSE_PROJECT_NAME=mailcow-dockerized" >> mailcow.conf echo "COMPOSE_PROJECT_NAME=mailcow-dockerized" >> mailcow.conf
fi fi
elif [[ ${option} == "DOVEADM_PORT" ]]; then elif [[ ${option} == "DOVEADM_PORT" ]]; then
if ! grep -q ${option} mailcow.conf; then if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf" echo "Adding new option \"${option}\" to mailcow.conf"
echo "DOVEADM_PORT=127.0.0.1:19991" >> mailcow.conf echo "DOVEADM_PORT=127.0.0.1:19991" >> mailcow.conf
fi fi
elif [[ ${option} == "WATCHDOG_NOTIFY_EMAIL" ]]; then elif [[ ${option} == "WATCHDOG_NOTIFY_EMAIL" ]]; then
if ! grep -q ${option} mailcow.conf; then if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf" echo "Adding new option \"${option}\" to mailcow.conf"
echo "WATCHDOG_NOTIFY_EMAIL=" >> mailcow.conf echo "WATCHDOG_NOTIFY_EMAIL=" >> mailcow.conf
fi fi
elif [[ ${option} == "LOG_LINES" ]]; then elif [[ ${option} == "LOG_LINES" ]]; then
if ! grep -q ${option} mailcow.conf; then if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf" 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 echo "LOG_LINES=9999" >> mailcow.conf
fi fi
elif ! grep -q ${option} mailcow.conf; then elif [[ ${option} == "IPV4_NETWORK" ]]; then
echo "Adding new option \"${option}\" to mailcow.conf" if ! grep -q ${option} mailcow.conf; then
echo "${option}=n" >> mailcow.conf echo "Adding new option \"${option}\" to mailcow.conf"
fi 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 done
echo -en "Checking internet connection... " echo -en "Checking internet connection... "
curl -o /dev/null google.com -sm3 curl -o /dev/null google.com -sm3
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
echo -e "\e[31mfailed\e[0m" echo -e "\e[31mfailed\e[0m"
exit 1 exit 1
else else
echo -e "\e[32mOK\e[0m" echo -e "\e[32mOK\e[0m"
fi fi
set -o pipefail set -o pipefail
export LC_ALL=C export LC_ALL=C
DATE=$(date +%Y-%m-%d_%H_%M_%S) DATE=$(date +%Y-%m-%d_%H_%M_%S)
BRANCH=$(git rev-parse --abbrev-ref HEAD) BRANCH=$(git rev-parse --abbrev-ref HEAD)
declare -a DC_PARAMS
case "${1}" in while (($#)); do
--check|-c) case "${1}" in
echo "Checking remote code for updates..." --check|-c)
git fetch origin ${BRANCH} echo "Checking remote code for updates..."
if ! git diff origin/${BRANCH} --quiet; then git fetch origin ${BRANCH}
echo "Updated code is available." if [[ $(git branch ${BRANCH} --contains $(git rev-parse origin/${BRANCH}) > /dev/null 2> /dev/null; echo $?) != 0 ]]; then
exit 0 echo "Updated code is available."
else exit 0
echo "No updates available." else
exit 3 echo "No updates available."
fi exit 3
;; fi
esac ;;
--no-start)
DC_PARAMS=(${DC_PARAMS[@]} "--no-start")
;;
esac
done
echo -e "\e[32mChecking for newer update script...\e[0m" echo -e "\e[32mChecking for newer update script...\e[0m"
SHA1_1=$(sha1sum update.sh) SHA1_1=$(sha1sum update.sh)
@ -74,22 +93,22 @@ git fetch origin ${BRANCH}
git checkout origin/${BRANCH} update.sh git checkout origin/${BRANCH} update.sh
SHA1_2=$(sha1sum update.sh) SHA1_2=$(sha1sum update.sh)
if [[ ${SHA1_1} != ${SHA1_2} ]]; then if [[ ${SHA1_1} != ${SHA1_2} ]]; then
echo "update.sh changed, please run this script again, exiting." echo "update.sh changed, please run this script again, exiting."
chmod +x update.sh chmod +x update.sh
exit 0 exit 0
fi fi
if [[ -f mailcow.conf ]]; then if [[ -f mailcow.conf ]]; then
source mailcow.conf source mailcow.conf
else else
echo -e "\e[31mNo mailcow.conf - is mailcow installed?\e[0m" echo -e "\e[31mNo mailcow.conf - is mailcow installed?\e[0m"
exit 1 exit 1
fi fi
read -r -p "Are you sure you want to update mailcow: dockerized? All containers will be stopped. [y/N] " response 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 if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "OK, exiting." echo "OK, exiting."
exit 0 exit 0
fi fi
echo -e "Stopping mailcow... " 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 # Need to use a variable to not pass return codes of if checks
MERGE_RETURN=$? MERGE_RETURN=$?
if [[ ${MERGE_RETURN} == 128 ]]; then 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" 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 exit 1
elif [[ ${MERGE_RETURN} == 1 ]]; then elif [[ ${MERGE_RETURN} == 1 ]]; then
echo -e "\e[93mPotenial conflict, trying to fix...\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 status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v
git add -A git add -A
git commit -m "After update on ${DATE}" > /dev/null git commit -m "After update on ${DATE}" > /dev/null
git checkout . git checkout .
echo -e "\e[32mRemoved and recreated files if necessary.\e[0m" echo -e "\e[32mRemoved and recreated files if necessary.\e[0m"
elif [[ ${MERGE_RETURN} != 0 ]]; then elif [[ ${MERGE_RETURN} != 0 ]]; then
echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m" echo -e "\e[31m\nOh no, something went wrong. Please check the error message above.\e[0m"
echo echo
echo "Run docker-compose up -d to restart your stack without updates or try again after fixing the mentioned errors." echo "Run docker-compose up -d to restart your stack without updates or try again after fixing the mentioned errors."
exit 1 exit 1
fi fi
echo -e "\e[32mFetching new docker-compose version...\e[0m" echo -e "\e[32mFetching new docker-compose version...\e[0m"
sleep 2 sleep 2
if [[ $(curl -sL -w "%{http_code}" https://www.servercow.de/docker-compose/latest.php -o /dev/null) == "200" ]]; then 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) 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) 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) chmod +x $(which docker-compose)
else else
echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m" echo -e "\e[33mCannot determine latest docker-compose version, skipping...\e[0m"
fi fi
echo -e "\e[32mFetching new images, if any...\e[0m" 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" echo -e "\e[32mStarting mailcow...\e[0m"
sleep 2 sleep 2
docker-compose up -d --remove-orphans docker-compose up -d --remove-orphans ${DC_PARAMS[@]}
echo -e "\e[32mCollecting garbage...\e[0m" echo -e "\e[32mCollecting garbage...\e[0m"
IMGS_TO_DELETE=() IMGS_TO_DELETE=()
for container in $(grep -oP "image: \Kmailcow.+" docker-compose.yml); do for container in $(grep -oP "image: \Kmailcow.+" docker-compose.yml); do
REPOSITORY=${container/:*} REPOSITORY=${container/:*}
TAG=${container/*:} TAG=${container/*:}
V_MAIN=${container/*.} V_MAIN=${container/*.}
V_SUB=${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 }') if [[ $V_MAIN_EXISTING == "latest" ]]; then
for existing_tag in ${EXISTING_TAGS[@]}; do echo "Found deprecated label \"latest\" for repository $REPOSITORY, it should be deleted."
V_MAIN_EXISTING=${existing_tag/*.} IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
V_SUB_EXISTING=${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."
# Not an integer IMGS_TO_DELETE+=($REPOSITORY:$existing_tag)
[[ ! $V_MAIN_EXISTING =~ ^[0-9]+$ ]] && continue elif [[ $V_SUB_EXISTING -lt $V_SUB ]]; then
[[ ! $V_SUB_EXISTING =~ ^[0-9]+$ ]] && continue 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)
if [[ $V_MAIN_EXISTING == "latest" ]]; then fi
echo "Found deprecated label \"latest\" for repository $REPOSITORY, it should be deleted." done
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 done
if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then if [[ ! -z ${IMGS_TO_DELETE[*]} ]]; then
echo "Run the following command to delete unused image tags:" echo "Run the following command to delete unused image tags:"
echo echo
echo " docker rmi ${IMGS_TO_DELETE[*]}" echo " docker rmi ${IMGS_TO_DELETE[*]}"
echo echo
read -r -p "Do you want to delete old image tags right now? [y/N] " response read -r -p "Do you want to delete old image tags right now? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
docker rmi ${IMGS_TO_DELETE[*]} docker rmi ${IMGS_TO_DELETE[*]}
else else
echo "OK, skipped." echo "OK, skipped."
fi fi
fi fi
echo -e "\e[32mFurther cleanup...\e[0m" 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\"" 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\""