diff --git a/data/Dockerfiles/clamd/Dockerfile b/data/Dockerfiles/clamd/Dockerfile index ec56bf1d..86a89df4 100644 --- a/data/Dockerfiles/clamd/Dockerfile +++ b/data/Dockerfiles/clamd/Dockerfile @@ -6,20 +6,38 @@ LABEL maintainer "André Peters " COPY dl_files.sh bootstrap.sh ./ # Installation -RUN apk add --update \ - && apk add --no-cache clamav clamav-libunrar curl bash tini \ +ENV CLAMAV 0.99.3 + +RUN apk add --no-cache --virtual build-dependencies alpine-sdk ncurses-dev zlib-dev bzip2-dev pcre-dev linux-headers fts-dev libxml2-dev libressl-dev \ + && apk add --no-cache curl bash tini libxml2 libbz2 pcre fts libressl \ + && wget -O - https://www.clamav.net/downloads/production/clamav-${CLAMAV}.tar.gz | tar xfvz - \ + && cd clamav-${CLAMAV} \ + && LIBS=-lfts ./configure \ + --prefix=/usr \ + --libdir=/usr/lib \ + --sysconfdir=/etc/clamav \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info \ + --without-iconv \ + --disable-llvm \ + --with-user=clamav \ + --with-group=clamav \ + --with-dbdir=/var/lib/clamav \ + --enable-clamdtop \ + --enable-bigstack \ + --with-pcre \ + && make -j4 \ + && make install \ + && make clean \ + && cd .. && rm -rf clamav-${CLAMAV} \ + && apk del build-dependencies \ + && addgroup -S clamav \ + && adduser -S -D -h /var/lib/clamav -s /sbin/nologin -G clamav -g clamav clamav \ + && mkdir -p /run/clamav \ + && chown clamav:clamav /run/clamav \ && chmod +x /dl_files.sh \ && set -ex; /bin/bash /dl_files.sh \ - && mkdir /run/clamav \ - && chown clamav:clamav /run/clamav \ - && chmod 750 /run/clamav \ - && sed -i '/Foreground yes/s/^#//g' /etc/clamav/clamd.conf \ - && sed -i '/TCPSocket 3310/s/^#//g' /etc/clamav/clamd.conf \ - && sed -i 's#LogFile /var/log/clamav/clamd.log#LogFile /tmp/logpipe_clamd#g' /etc/clamav/clamd.conf \ - && sed -i 's/#PhishingSignatures yes/PhishingSignatures no/g' /etc/clamav/clamd.conf \ - && sed -i 's/#PhishingScanURLs yes/PhishingScanURLs no/g' /etc/clamav/clamd.conf \ - && sed -i 's#UpdateLogFile /var/log/clamav/freshclam.log#UpdateLogFile /tmp/logpipe_freshclam#g' /etc/clamav/freshclam.conf \ - && sed -i '/Foreground yes/s/^#//g' /etc/clamav/freshclam.conf + && chmod 750 /run/clamav # Port provision EXPOSE 3310 diff --git a/data/Dockerfiles/dovecot/imapsync_cron.pl b/data/Dockerfiles/dovecot/imapsync_cron.pl index ddd4746a..db0796ed 100755 --- a/data/Dockerfiles/dovecot/imapsync_cron.pl +++ b/data/Dockerfiles/dovecot/imapsync_cron.pl @@ -33,26 +33,29 @@ open my $file, '<', "/etc/sogo/sieve.creds"; my $creds = <$file>; close $file; my ($master_user, $master_pass) = split /:/, $creds; -my $sth = $dbh->prepare("SELECT id, user1, user2, host1, authmech1, password1, exclude, port1, enc1, delete2duplicates, maxage, subfolder2, delete1, delete2 FROM imapsync WHERE active = 1 AND (UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(last_run) > mins_interval * 60 OR last_run IS NULL) ORDER BY last_run"); +my $sth = $dbh->prepare("SELECT id, user1, user2, host1, authmech1, password1, exclude, port1, enc1, delete2duplicates, maxage, subfolder2, delete1, delete2, automap, skipcrossduplicates, maxbytespersecond FROM imapsync WHERE active = 1 AND (UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(last_run) > mins_interval * 60 OR last_run IS NULL) ORDER BY last_run"); $sth->execute(); my $row; while ($row = $sth->fetchrow_arrayref()) { - $id = @$row[0]; - $user1 = @$row[1]; - $user2 = @$row[2]; - $host1 = @$row[3]; - $authmech1 = @$row[4]; - $password1 = @$row[5]; - $exclude = @$row[6]; - $port1 = @$row[7]; - $enc1 = @$row[8]; - $delete2duplicates = @$row[9]; - $maxage = @$row[10]; - $subfolder2 = @$row[11]; - $delete1 = @$row[12]; - $delete2 = @$row[13]; + $id = @$row[0]; + $user1 = @$row[1]; + $user2 = @$row[2]; + $host1 = @$row[3]; + $authmech1 = @$row[4]; + $password1 = @$row[5]; + $exclude = @$row[6]; + $port1 = @$row[7]; + $enc1 = @$row[8]; + $delete2duplicates = @$row[9]; + $maxage = @$row[10]; + $subfolder2 = @$row[11]; + $delete1 = @$row[12]; + $delete2 = @$row[13]; + $automap = @$row[14]; + $skipcrossduplicates = @$row[15]; + $maxbytespersecond = @$row[16]; $is_running = $dbh->prepare("UPDATE imapsync SET is_running = 1 WHERE id = ?"); $is_running->bind_param( 1, ${id} ); @@ -72,11 +75,14 @@ while ($row = $sth->fetchrow_arrayref()) { "--tmpdir", "/tmp", "--subscribeall", ($exclude eq "" ? () : ("--exclude", $exclude)), - ($subfolder2 eq "" ? () : ('--subfolder2', $subfolder2)), - ($maxage eq "0" ? () : ('--maxage', $maxage)), - ($delete2duplicates ne "1" ? () : ('--delete2duplicates')), - ($delete1 ne "1" ? () : ('--delete')), - ($delete2 ne "1" ? () : ('--delete2')), + ($subfolder2 eq "" ? () : ('--subfolder2', $subfolder2)), + ($maxage eq "0" ? () : ('--maxage', $maxage)), + ($maxbytespersecond eq "0" ? () : ('--maxbytespersecond', $maxage)), + ($delete2duplicates ne "1" ? () : ('--delete2duplicates')), + ($delete1 ne "1" ? () : ('--delete')), + ($delete2 ne "1" ? () : ('--delete2')), + ($automap ne "1" ? () : ('--automap')), + ($skipcrossduplicates ne "1" ? () : ('--skipcrossduplicates')), (!defined($enc1) ? () : ($enc1)), "--host1", $host1, "--user1", $user1, diff --git a/data/Dockerfiles/fail2ban/logwatch.py b/data/Dockerfiles/fail2ban/logwatch.py index 10024772..e6f6d099 100644 --- a/data/Dockerfiles/fail2ban/logwatch.py +++ b/data/Dockerfiles/fail2ban/logwatch.py @@ -14,11 +14,11 @@ import json yes_regex = re.compile(r'([yY][eE][sS]|[yY])+$') if re.search(yes_regex, os.getenv('SKIP_FAIL2BAN', 0)): - print "SKIP_FAIL2BAN=y, Skipping Fail2ban container..." + print 'SKIP_FAIL2BAN=y, Skipping Fail2ban container...' time.sleep(31536000) raise SystemExit -r = redis.StrictRedis(host='172.22.1.249', decode_responses=True, port=6379, db=0) +r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0) pubsub = r.pubsub() RULES = {} @@ -29,19 +29,23 @@ RULES[4] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=( RULES[5] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked' RULES[6] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)' -r.setnx("F2B_BAN_TIME", "1800") -r.setnx("F2B_MAX_ATTEMPTS", "10") -r.setnx("F2B_RETRY_WINDOW", "600") +r.setnx('F2B_BAN_TIME', '1800') +r.setnx('F2B_MAX_ATTEMPTS', '10') +r.setnx('F2B_RETRY_WINDOW', '600') +r.setnx('F2B_NETBAN_IPV6', '64') +r.setnx('F2B_NETBAN_IPV4', '24') bans = {} log = {} quit_now = False def ban(address): - BAN_TIME = int(r.get("F2B_BAN_TIME")) - MAX_ATTEMPTS = int(r.get("F2B_MAX_ATTEMPTS")) - RETRY_WINDOW = int(r.get("F2B_RETRY_WINDOW")) - WHITELIST = r.hgetall("F2B_WHITELIST") + BAN_TIME = int(r.get('F2B_BAN_TIME')) + MAX_ATTEMPTS = int(r.get('F2B_MAX_ATTEMPTS')) + RETRY_WINDOW = int(r.get('F2B_RETRY_WINDOW')) + WHITELIST = r.hgetall('F2B_WHITELIST') + NETBAN_IPV6 = '/' + str(r.get('F2B_NETBAN_IPV6')) + NETBAN_IPV4 = '/' + str(r.get('F2B_NETBAN_IPV4')) ip = ipaddress.ip_address(address.decode('ascii')) if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped: @@ -56,13 +60,13 @@ def ban(address): wl_net = ipaddress.ip_network(wl_key.decode('ascii'), False) if wl_net.overlaps(self_network): log['time'] = int(round(time.time())) - log['priority'] = "info" - log['message'] = "Address %s is whitelisted by rule %s" % (self_network, wl_net) - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "Address %s is whitelisted by rule %s" % (self_network, wl_net) + log['priority'] = 'info' + log['message'] = 'Address %s is whitelisted by rule %s' % (self_network, wl_net) + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print 'Address %s is whitelisted by rule %s' % (self_network, wl_net) return - net = ipaddress.ip_network((address + ('/24' if type(ip) is ipaddress.IPv4Address else '/64')).decode('ascii'), strict=False) + net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)).decode('ascii'), strict=False) net = str(net) if not net in bans or time.time() - bans[net]['last_attempt'] > RETRY_WINDOW: @@ -78,45 +82,45 @@ def ban(address): if bans[net]['attempts'] >= MAX_ATTEMPTS: log['time'] = int(round(time.time())) - log['priority'] = "crit" - log['message'] = "Banning %s" % net - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "Banning %s for %d minutes" % (net, BAN_TIME / 60) + log['priority'] = 'crit' + log['message'] = 'Banning %s' % net + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print 'Banning %s for %d minutes' % (net, BAN_TIME / 60) if type(ip) is ipaddress.IPv4Address: - subprocess.call(["iptables", "-I", "INPUT", "-s", net, "-j", "REJECT"]) - subprocess.call(["iptables", "-I", "FORWARD", "-s", net, "-j", "REJECT"]) + subprocess.call(['iptables', '-I', 'INPUT', '-s', net, '-j', 'REJECT']) + subprocess.call(['iptables', '-I', 'FORWARD', '-s', net, '-j', 'REJECT']) else: - subprocess.call(["ip6tables", "-I", "INPUT", "-s", net, "-j", "REJECT"]) - subprocess.call(["ip6tables", "-I", "FORWARD", "-s", net, "-j", "REJECT"]) - r.hset("F2B_ACTIVE_BANS", "%s" % net, log['time'] + BAN_TIME) + subprocess.call(['ip6tables', '-I', 'INPUT', '-s', net, '-j', 'REJECT']) + subprocess.call(['ip6tables', '-I', 'FORWARD', '-s', net, '-j', 'REJECT']) + r.hset('F2B_ACTIVE_BANS', '%s' % net, log['time'] + BAN_TIME) else: log['time'] = int(round(time.time())) - log['priority'] = "warn" - log['message'] = "%d more attempts in the next %d seconds until %s is banned" % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net) - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "%d more attempts in the next %d seconds until %s is banned" % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net) + log['priority'] = 'warn' + log['message'] = '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net) + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net) def unban(net): log['time'] = int(round(time.time())) - log['priority'] = "info" - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) + log['priority'] = 'info' + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) if not net in bans: - log['message'] = "%s is not banned, skipping unban and deleting from queue (if any)" % net - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "%s is not banned, skipping unban and deleting from queue (if any)" % net - r.hdel("F2B_QUEUE_UNBAN", "%s" % net) + log['message'] = '%s is not banned, skipping unban and deleting from queue (if any)' % net + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print '%s is not banned, skipping unban and deleting from queue (if any)' % net + r.hdel('F2B_QUEUE_UNBAN', '%s' % net) return - log['message'] = "Unbanning %s" % net - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "Unbanning %s" % net + log['message'] = 'Unbanning %s' % net + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print 'Unbanning %s' % net if type(ipaddress.ip_network(net.decode('ascii'))) is ipaddress.IPv4Network: - subprocess.call(["iptables", "-D", "INPUT", "-s", net, "-j", "REJECT"]) - subprocess.call(["iptables", "-D", "FORWARD", "-s", net, "-j", "REJECT"]) + subprocess.call(['iptables', '-D', 'INPUT', '-s', net, '-j', 'REJECT']) + subprocess.call(['iptables', '-D', 'FORWARD', '-s', net, '-j', 'REJECT']) else: - subprocess.call(["ip6tables", "-D", "INPUT", "-s", net, "-j", "REJECT"]) - subprocess.call(["ip6tables", "-D", "FORWARD", "-s", net, "-j", "REJECT"]) - r.hdel("F2B_ACTIVE_BANS", "%s" % net) - r.hdel("F2B_QUEUE_UNBAN", "%s" % net) + subprocess.call(['ip6tables', '-D', 'INPUT', '-s', net, '-j', 'REJECT']) + subprocess.call(['ip6tables', '-D', 'FORWARD', '-s', net, '-j', 'REJECT']) + r.hdel('F2B_ACTIVE_BANS', '%s' % net) + r.hdel('F2B_QUEUE_UNBAN', '%s' % net) del bans[net] def quit(signum, frame): @@ -125,21 +129,21 @@ def quit(signum, frame): def clear(): log['time'] = int(round(time.time())) - log['priority'] = "info" - log['message'] = "Clearing all bans" - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - print "Clearing all bans" + log['priority'] = 'info' + log['message'] = 'Clearing all bans' + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + print 'Clearing all bans' for net in bans.copy(): unban(net) pubsub.unsubscribe() def watch(): log['time'] = int(round(time.time())) - log['priority'] = "info" - log['message'] = "Watching Redis channel F2B_CHANNEL" - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) - pubsub.subscribe("F2B_CHANNEL") - print "Subscribing to Redis channel F2B_CHANNEL" + log['priority'] = 'info' + log['message'] = 'Watching Redis channel F2B_CHANNEL' + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) + pubsub.subscribe('F2B_CHANNEL') + print 'Subscribing to Redis channel F2B_CHANNEL' while True: for item in pubsub.listen(): for rule_id, rule_regex in RULES.iteritems(): @@ -150,18 +154,18 @@ def watch(): ip = ipaddress.ip_address(addr.decode('ascii')) if ip.is_private or ip.is_loopback: continue - print "%s matched rule id %d" % (addr, rule_id) + print '%s matched rule id %d' % (addr, rule_id) log['time'] = int(round(time.time())) - log['priority'] = "warn" - log['message'] = "%s matched rule id %d" % (addr, rule_id) - r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False)) + log['priority'] = 'warn' + log['message'] = '%s matched rule id %d' % (addr, rule_id) + r.lpush('F2B_LOG', json.dumps(log, ensure_ascii=False)) ban(addr) def autopurge(): while not quit_now: - BAN_TIME = int(r.get("F2B_BAN_TIME")) - MAX_ATTEMPTS = int(r.get("F2B_MAX_ATTEMPTS")) - QUEUE_UNBAN = r.hgetall("F2B_QUEUE_UNBAN") + BAN_TIME = int(r.get('F2B_BAN_TIME')) + MAX_ATTEMPTS = int(r.get('F2B_MAX_ATTEMPTS')) + QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN') if QUEUE_UNBAN: for net in QUEUE_UNBAN: unban(str(net)) diff --git a/data/Dockerfiles/phpfpm/Dockerfile b/data/Dockerfiles/phpfpm/Dockerfile index 5259e70d..45d2fa84 100644 --- a/data/Dockerfiles/phpfpm/Dockerfile +++ b/data/Dockerfiles/phpfpm/Dockerfile @@ -1,10 +1,11 @@ FROM php:7.1-fpm-alpine LABEL maintainer "Andre Peters " -ENV REDIS_PECL 3.1.4 -ENV MEMCACHED_PECL 3.0.3 -ENV APCU_PECL 5.1.8 +ENV REDIS_PECL 3.1.6 +ENV MEMCACHED_PECL 3.0.4 +ENV APCU_PECL 5.1.9 ENV IMAGICK_PECL 3.4.3 +ENV MAILPARSE_PECL 3.0.2 RUN apk add -U --no-cache libxml2-dev \ icu-dev \ @@ -41,27 +42,16 @@ RUN apk add -U --no-cache libxml2-dev \ Net_Sieve \ NET_SMTP \ Mail_mime \ - && pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} mailparse \ + && pecl install redis-${REDIS_PECL} memcached-${MEMCACHED_PECL} APCu-${APCU_PECL} imagick-${IMAGICK_PECL} mailparse-${MAILPARSE_PECL} \ && docker-php-ext-enable redis apcu memcached imagick mailparse \ && pecl clear-cache \ && docker-php-ext-configure intl \ && docker-php-ext-install -j 4 intl gettext ldap sockets soap pdo pdo_mysql xmlrpc gd zip pcntl opcache \ && docker-php-ext-configure imap --with-imap --with-imap-ssl \ && docker-php-ext-install -j 4 imap \ - && apk del --purge autoconf g++ make libxml2-dev icu-dev imap-dev openssl-dev cyrus-sasl-dev pcre-dev libpng-dev libpng-dev libjpeg-turbo-dev libwebp-dev zlib-dev imagemagick-dev \ - && { \ - echo 'opcache.enable=1'; \ - echo 'opcache.enable_cli=1'; \ - echo 'opcache.interned_strings_buffer=8'; \ - echo 'opcache.max_accelerated_files=10000'; \ - echo 'opcache.memory_consumption=128'; \ - echo 'opcache.save_comments=1'; \ - echo 'opcache.revalidate_freq=1'; \ -} > /usr/local/etc/php/conf.d/opcache-recommended.ini + && apk del --purge autoconf g++ make libxml2-dev icu-dev imap-dev openssl-dev cyrus-sasl-dev pcre-dev libpng-dev libpng-dev libjpeg-turbo-dev libwebp-dev zlib-dev imagemagick-dev COPY ./docker-entrypoint.sh / -EXPOSE 9000 - ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["php-fpm"] diff --git a/data/Dockerfiles/postfix/Dockerfile b/data/Dockerfiles/postfix/Dockerfile index 2e0e73df..43ed1c27 100644 --- a/data/Dockerfiles/postfix/Dockerfile +++ b/data/Dockerfiles/postfix/Dockerfile @@ -26,7 +26,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ syslog-ng-core \ syslog-ng-mod-redis \ && rm -rf /var/lib/apt/lists/* \ - && touch /etc/default/locale + && touch /etc/default/locale \ + && printf '#!/bin/bash\n/usr/sbin/postconf -c /opt/postfix/conf "$@"' > /usr/local/sbin/postconf \ + && chmod +x /usr/local/sbin/postconf RUN addgroup --system --gid 600 zeyple RUN adduser --system --home /var/lib/zeyple --no-create-home --uid 600 --gid 600 --disabled-login zeyple diff --git a/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh b/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh index ab066d89..4ad5ab32 100755 --- a/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh +++ b/data/Dockerfiles/postfix/whitelist_forwardinghosts.sh @@ -6,7 +6,7 @@ while read QUERY; do echo "500 dunno" continue fi - result=$(curl -s http://172.22.1.251:8081/forwardinghosts.php?host=${QUERY[1]}) + result=$(curl -s http://nginx:8081/forwardinghosts.php?host=${QUERY[1]}) logger -t whitelist_forwardinghosts -p mail.info "Look up ${QUERY[1]} on whitelist, result $result" echo ${result} done diff --git a/data/Dockerfiles/rspamd/settings.conf b/data/Dockerfiles/rspamd/settings.conf index 3c1c9c16..4449f091 100644 --- a/data/Dockerfiles/rspamd/settings.conf +++ b/data/Dockerfiles/rspamd/settings.conf @@ -1 +1 @@ -settings = "http://172.22.1.251:8081/settings.php"; +settings = "http://nginx:8081/settings.php"; diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh index 4bd9d570..59877927 100755 --- a/data/Dockerfiles/sogo/bootstrap-sogo.sh +++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh @@ -18,11 +18,13 @@ done mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP VIEW IF EXISTS sogo_view" mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF -CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, home, kind, multiple_bookings) AS -SELECT mailbox.username, mailbox.domain, mailbox.username, mailbox.password, mailbox.name, mailbox.username, IFNULL(ga.aliases, ''), IFNULL(gda.ad_alias, ''), CONCAT('/var/vmail/', maildir), mailbox.kind, mailbox.multiple_bookings FROM mailbox -LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username = mailbox.username +CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, sa_aliases, ad_aliases, home, kind, multiple_bookings) AS +SELECT mailbox.username, mailbox.domain, mailbox.username, mailbox.password, mailbox.name, mailbox.username, IFNULL(GROUP_CONCAT(ga.aliases SEPARATOR ' '), ''), IFNULL(gsa.send_as_acl, ''), IFNULL(gda.ad_alias, ''), CONCAT('/var/vmail/', maildir), mailbox.kind, mailbox.multiple_bookings FROM mailbox +LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username REGEXP CONCAT('(^|,)', mailbox.username, '($|,)') +LEFT OUTER JOIN grouped_sender_acl gsa ON gsa.username = mailbox.username LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username -WHERE mailbox.active = '1'; +WHERE mailbox.active = '1' +GROUP BY mailbox.username; EOF @@ -67,6 +69,7 @@ while read line MailFieldNames aliases + sa_aliases ad_aliases KindFieldName diff --git a/data/Dockerfiles/watchdog/watchdog.sh b/data/Dockerfiles/watchdog/watchdog.sh index 1302f49a..bed11907 100755 --- a/data/Dockerfiles/watchdog/watchdog.sh +++ b/data/Dockerfiles/watchdog/watchdog.sh @@ -191,6 +191,7 @@ phpfpm_checks() { host_ip=$(get_container_ip php-fpm-mailcow) err_c_cur=${err_count} cgi-fcgi -bind -connect ${host_ip}:9000 | grep "Content-type" 1>&2; err_count=$(( ${err_count} + ($? * 2))) + cgi-fcgi -bind -connect ${host_ip}:9001 | grep "Content-type" 1>&2; err_count=$(( ${err_count} + ($? * 2))) /usr/lib/nagios/plugins/check_ping -4 -H ${host_ip} -w 2000,10% -c 4000,100% -p2 1>&2; err_count=$(( ${err_count} + $? )) [ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1 [ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} )) diff --git a/data/assets/nextcloud/nextcloud.conf b/data/assets/nextcloud/nextcloud.conf index 2eaf6faa..2fa9d9d8 100644 --- a/data/assets/nextcloud/nextcloud.conf +++ b/data/assets/nextcloud/nextcloud.conf @@ -25,7 +25,7 @@ server { add_header X-Download-Options noopen; add_header X-Permitted-Cross-Domain-Policies none; - server_name NC_SERVER_SUB; + server_name NC_SUBD; root /web/nextcloud/; @@ -38,18 +38,26 @@ server { location = /.well-known/carddav { return 301 $client_req_scheme_nc://$host/remote.php/dav; } + location = /.well-known/caldav { return 301 $client_req_scheme_nc://$host/remote.php/dav; } + location ^~ /.well-known/acme-challenge/ { + default_type "text/plain"; + root /web; + } + gzip on; gzip_vary on; gzip_comp_level 4; gzip_min_length 256; gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; - - set_real_ip_from 172.22.1.1; + set_real_ip_from fd00::/8; + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; real_ip_header X-Forwarded-For; real_ip_recursive on; @@ -100,88 +108,3 @@ server { access_log off; } } -server { - include /etc/nginx/conf.d/listen_ssl.active; - include /etc/nginx/mime.types; - charset utf-8; - override_charset on; - - add_header X-Content-Type-Options nosniff; - add_header X-XSS-Protection "1; mode=block"; - add_header X-Robots-Tag none; - add_header X-Download-Options noopen; - add_header X-Permitted-Cross-Domain-Policies none; - - server_name NC_SERVER_SUB; - - root /web/nextcloud/; - - location = /robots.txt { - allow all; - log_not_found off; - access_log off; - } - - location = /.well-known/carddav { - return 301 $client_req_scheme_nc://$host/remote.php/dav; - } - location = /.well-known/caldav { - return 301 $client_req_scheme_nc://$host/remote.php/dav; - } - - gzip on; - gzip_vary on; - gzip_comp_level 4; - gzip_min_length 256; - gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; - gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; - - set_real_ip_from 172.22.1.1; - real_ip_header X-Forwarded-For; - real_ip_recursive on; - - location / { - rewrite ^ /index.php$uri; - } - - location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ { - deny all; - } - - location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { - deny all; - } - - location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) { - fastcgi_split_path_info ^(.+\.php)(/.*)$; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param modHeadersAvailable true; - fastcgi_param front_controller_active true; - fastcgi_pass phpfpm:9000; - fastcgi_intercept_errors on; - fastcgi_request_buffering off; - } - - location ~ ^/(?:updater|ocs-provider)(?:$|/) { - try_files $uri/ =404; - index index.php; - } - - location ~ \.(?:css|js|woff|svg|gif)$ { - try_files $uri /index.php$uri$is_args$args; - add_header Cache-Control "public, max-age=15778463"; - add_header X-Content-Type-Options nosniff; - add_header X-XSS-Protection "1; mode=block"; - add_header X-Robots-Tag none; - add_header X-Download-Options noopen; - add_header X-Permitted-Cross-Domain-Policies none; - access_log off; - } - - location ~ \.(?:png|html|ttf|ico|jpg|jpeg)$ { - try_files $uri /index.php$uri$is_args$args; - access_log off; - } -} diff --git a/data/conf/clamav/clamd.conf b/data/conf/clamav/clamd.conf new file mode 100644 index 00000000..362ae5a5 --- /dev/null +++ b/data/conf/clamav/clamd.conf @@ -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 diff --git a/data/conf/clamav/freshclam.conf b/data/conf/clamav/freshclam.conf new file mode 100644 index 00000000..382befbc --- /dev/null +++ b/data/conf/clamav/freshclam.conf @@ -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 + diff --git a/data/conf/nginx/dynmaps.conf b/data/conf/nginx/dynmaps.conf index 3ff8692f..99c0c6aa 100644 --- a/data/conf/nginx/dynmaps.conf +++ b/data/conf/nginx/dynmaps.conf @@ -10,7 +10,7 @@ server { location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass phpfpm:9000; + fastcgi_pass phpfpm:9001; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf index 89572eff..8a896264 100644 --- a/data/conf/nginx/site.conf +++ b/data/conf/nginx/site.conf @@ -7,6 +7,13 @@ map $http_x_forwarded_proto $client_req_scheme { https https; } +server { + listen 80 default_server; + listen [::]:80 default_server; + include /etc/nginx/conf.d/server_name.active; + return 301 https://$host$request_uri; +} + server { include /etc/nginx/mime.types; charset utf-8; @@ -17,6 +24,7 @@ server { add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; add_header X-Download-Options noopen; + add_header X-Frame-Options "SAMEORIGIN"; add_header X-Permitted-Cross-Domain-Policies none; index index.php index.html; @@ -39,7 +47,10 @@ server { } # If behind reverse proxy, forwards the correct IP - set_real_ip_from 172.22.1.1; + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; + set_real_ip_from fd00::/8; real_ip_header X-Forwarded-For; real_ip_recursive on; @@ -65,7 +76,7 @@ server { } location /rspamd/ { - proxy_pass http://172.22.1.253:11334/; + proxy_pass http://rspamd:11334/; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; @@ -97,7 +108,7 @@ server { } location ^~ /Microsoft-Server-ActiveSync { - proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync; + proxy_pass http://sogo:20000/SOGo/Microsoft-Server-ActiveSync; proxy_connect_timeout 1000; proxy_next_upstream timeout error; proxy_send_timeout 1000; @@ -119,7 +130,7 @@ server { } location ^~ /SOGo { - proxy_pass http://172.22.1.252:20000; + proxy_pass http://sogo:20000; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; @@ -134,7 +145,7 @@ server { } location /SOGo.woa/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -144,7 +155,7 @@ server { } location /.woa/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -154,7 +165,7 @@ server { } location /SOGo/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -164,7 +175,7 @@ server { } location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ { - proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2; + proxy_pass http://sogo:9192/$1.SOGo/Resources/$2; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -195,6 +206,7 @@ server { add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; add_header X-Download-Options noopen; + add_header X-Frame-Options "SAMEORIGIN"; add_header X-Permitted-Cross-Domain-Policies none; index index.php index.html; @@ -217,7 +229,10 @@ server { } # If behind reverse proxy, forwards the correct IP - set_real_ip_from 172.22.1.1; + set_real_ip_from 10.0.0.0/8; + set_real_ip_from 172.16.0.0/12; + set_real_ip_from 192.168.0.0/16; + set_real_ip_from fd00::/8; real_ip_header X-Forwarded-For; real_ip_recursive on; @@ -243,7 +258,7 @@ server { } location /rspamd/ { - proxy_pass http://172.22.1.253:11334/; + proxy_pass http://rspamd:11334/; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; @@ -275,7 +290,7 @@ server { } location ^~ /Microsoft-Server-ActiveSync { - proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync; + proxy_pass http://sogo:20000/SOGo/Microsoft-Server-ActiveSync; proxy_connect_timeout 1000; proxy_next_upstream timeout error; proxy_send_timeout 1000; @@ -297,7 +312,7 @@ server { } location ^~ /SOGo { - proxy_pass http://172.22.1.252:20000; + proxy_pass http://sogo:20000; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; @@ -312,7 +327,7 @@ server { } location /SOGo.woa/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -322,7 +337,7 @@ server { } location /.woa/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -332,7 +347,7 @@ server { } location /SOGo/WebServerResources/ { - proxy_pass http://172.22.1.252:9192/WebServerResources/; + proxy_pass http://sogo:9192/WebServerResources/; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; @@ -342,7 +357,7 @@ server { } location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ { - proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2; + proxy_pass http://sogo:9192/$1.SOGo/Resources/$2; proxy_set_header Host $http_host; proxy_cache sogo; proxy_cache_valid 200 1d; diff --git a/data/conf/phpfpm/php-conf.d/opcache-recommended.ini b/data/conf/phpfpm/php-conf.d/opcache-recommended.ini new file mode 100644 index 00000000..104f2425 --- /dev/null +++ b/data/conf/phpfpm/php-conf.d/opcache-recommended.ini @@ -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 diff --git a/data/conf/phpfpm/php-fpm.d/system.conf b/data/conf/phpfpm/php-fpm.d/system.conf new file mode 100644 index 00000000..52edd73c --- /dev/null +++ b/data/conf/phpfpm/php-fpm.d/system.conf @@ -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 diff --git a/data/conf/phpfpm/php-fpm.d/www.conf b/data/conf/phpfpm/php-fpm.d/www.conf new file mode 100644 index 00000000..e341c3c7 --- /dev/null +++ b/data/conf/phpfpm/php-fpm.d/www.conf @@ -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 diff --git a/data/conf/postfix/main.cf b/data/conf/postfix/main.cf index 1c943dc6..0599c706 100644 --- a/data/conf/postfix/main.cf +++ b/data/conf/postfix/main.cf @@ -9,7 +9,7 @@ smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_una alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases relayhost = -mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 [fd4d:6169:6c63:6f77::]/64 +mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 [fd::]/8 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = all diff --git a/data/conf/rspamd/dynmaps/settings.php b/data/conf/rspamd/dynmaps/settings.php index 99a72a53..3ec8ddac 100644 --- a/data/conf/rspamd/dynmaps/settings.php +++ b/data/conf/rspamd/dynmaps/settings.php @@ -196,7 +196,7 @@ while ($row = array_shift($rows)) { } whitelist_header_ { prepare("SELECT GROUP_CONCAT(REPLACE(CONCAT('^', `value`, '$'), '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf` + $stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(CONCAT('\<', `value`, '\>'), '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf` WHERE `object`= :object AND `option` = 'whitelist_from'"); $stmt->execute(array(':object' => $row['object'])); @@ -288,7 +288,7 @@ while ($row = array_shift($rows)) { } blacklist_header_ { prepare("SELECT GROUP_CONCAT(REPLACE(CONCAT('^', `value`, '$'), '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf` + $stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(CONCAT('\<', `value`, '\>'), '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf` WHERE `object`= :object AND `option` = 'blacklist_from'"); $stmt->execute(array(':object' => $row['object'])); diff --git a/data/conf/rspamd/local.d/greylist.conf b/data/conf/rspamd/local.d/greylist.conf index 9acf6f28..ee0220d7 100644 --- a/data/conf/rspamd/local.d/greylist.conf +++ b/data/conf/rspamd/local.d/greylist.conf @@ -1 +1 @@ -whitelisted_ip = "http://172.22.1.251:8081/forwardinghosts.php"; +whitelisted_ip = "http://nginx:8081/forwardinghosts.php"; diff --git a/data/conf/rspamd/override.d/worker-controller.inc b/data/conf/rspamd/override.d/worker-controller.inc index 22d9a024..7f17485d 100644 --- a/data/conf/rspamd/override.d/worker-controller.inc +++ b/data/conf/rspamd/override.d/worker-controller.inc @@ -4,6 +4,6 @@ secure_ip = "172.16.0.0/12"; secure_ip = "10.0.0.0/8"; secure_ip = "127.0.0.1"; secure_ip = "::1"; -secure_ip = "fd4d:6169:6c63:6f77::/64" +secure_ip = "fd00::/8" .include(try=true; priority=10) "$CONFDIR/override.d/worker-controller-password.inc" .include(try=true; priority=20) "$CONFDIR/override.d/worker-controller.custom.inc" diff --git a/data/conf/sogo/sogo.conf b/data/conf/sogo/sogo.conf index bf79358a..0acb8a80 100644 --- a/data/conf/sogo/sogo.conf +++ b/data/conf/sogo/sogo.conf @@ -5,7 +5,7 @@ PrivateDAndTViewer ); - WOWorkersCount = "20"; + WOWorkersCount = "7"; SOGoACLsSendEMailNotifications = YES; SOGoAppointmentSendEMailNotifications = YES; SOGoDraftsFolderName = "Drafts"; @@ -36,7 +36,7 @@ SOGoMailingMechanism = smtp; SOGoSMTPAuthenticationType = plain; - SxVMemLimit = 512; + SxVMemLimit = 384; SOGoMaximumPingInterval = 354; diff --git a/data/conf/unbound/unbound.conf b/data/conf/unbound/unbound.conf index a668d0d8..16952ff2 100644 --- a/data/conf/unbound/unbound.conf +++ b/data/conf/unbound/unbound.conf @@ -8,8 +8,11 @@ server: do-udp: yes do-tcp: yes do-daemonize: no - access-control: 172.22.1.0/24 allow - access-control: fd4d:6169:6c63:6f77::/64 allow + access-control: 10.0.0.0/8 allow + access-control: 172.16.0.0/12 allow + access-control: 192.168.0.0/16 allow + access-control: fd00::/8 allow + access-control: fe80::/10 allow directory: "/etc/unbound" username: unbound auto-trust-anchor-file: trusted-key.key @@ -19,7 +22,6 @@ server: private-address: 169.254.0.0/16 private-address: fd00::/8 private-address: fe80::/10 - private-address: fd4d:6169:6c63:6f77::/64 root-hints: "/etc/unbound/root.hints" hide-identity: yes hide-version: yes diff --git a/data/web/admin.php b/data/web/admin.php index 79bce8e3..6c0aabd5 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -168,29 +168,31 @@ $tfa_data = get_tfa(); if (!empty($dkim = dkim('details', $domain))) { ?>
-
-
+
+

Domain: -

-

Selector ''

-

bit

+

+

Selector ''

+

bit

-
+

↪ Private key

+
-
-
+
+

Domain:

-
-
+
-
+
-
-
+
+

↳ Alias-Domain: -

-

Selector ''

-

bit

+

+

Selector ''

+

bit

-
+

↪ Private key

+
-
-
+
+

↳ Alias-Domain:

-
-
+
-
+
-
-
+
+

Domain: -

-

Selector ''

-

bit

+

+

Selector ''

+

bit

-
+

↪ Private key

+
:
+
+ +
+ / + +
+
+
+ +
+ / + +
+
@@ -427,7 +446,7 @@ $tfa_data = get_tfa(); ?>
- +
@@ -441,7 +460,7 @@ $tfa_data = get_tfa();

- +

- +

@@ -526,7 +545,7 @@ $tfa_data = get_tfa();
- +
diff --git a/data/web/css/mailcow.css b/data/web/css/mailcow.css index e8358b04..1c6a856e 100644 --- a/data/web/css/mailcow.css +++ b/data/web/css/mailcow.css @@ -117,3 +117,22 @@ legend { .lang-link-disabled { cursor: not-allowed; } +.dkim-label { + margin: 0 0 2px !important; +} +.overlay { + background: #fff; + position: absolute; + z-index: 10000; + top: 0; right: 0; bottom: 0; left: 0; + opacity: 0.7; +} +nav .glyphicon { + font-size: 12px !important; +} +.logged-in-as { + border-left: 1px solid #E7E7E7; +} +#top { + padding-top: 70px; +} \ No newline at end of file diff --git a/data/web/edit.php b/data/web/edit.php index 5e337249..43e055ca 100644 --- a/data/web/edit.php +++ b/data/web/edit.php @@ -704,6 +704,8 @@ if (isset($_SESSION['mailcow_cc_role'])) { + +
@@ -743,6 +745,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
+ 10-3600
@@ -754,7 +757,15 @@ if (isset($_SESSION['mailcow_cc_role'])) {
- + + 0-32000 +
+
+
+ +
+ + 0-125000000
@@ -784,6 +795,20 @@ if (isset($_SESSION['mailcow_cc_role'])) {
+
+
+
+ +
+
+
+
+
+
+ +
+
+
diff --git a/data/web/inc/footer.inc.php b/data/web/inc/footer.inc.php index eac175e4..3c92c28e 100644 --- a/data/web/inc/footer.inc.php +++ b/data/web/inc/footer.inc.php @@ -22,7 +22,9 @@ function setLang(sel) { $.post( "", {lang: sel} ); window.location.href = window.location.pathname + window.location.search; } - +$(window).load(function() { + $(".overlay").hide(); +}); $(document).ready(function() { window.mailcow_alert_box = function(message, type) { msg = $('').html(message).text(); diff --git a/data/web/inc/functions.fail2ban.inc.php b/data/web/inc/functions.fail2ban.inc.php index e1801be7..b78fb36a 100644 --- a/data/web/inc/functions.fail2ban.inc.php +++ b/data/web/inc/functions.fail2ban.inc.php @@ -12,6 +12,8 @@ function fail2ban($_action, $_data = null) { $data['ban_time'] = $redis->Get('F2B_BAN_TIME'); $data['max_attempts'] = $redis->Get('F2B_MAX_ATTEMPTS'); $data['retry_window'] = $redis->Get('F2B_RETRY_WINDOW'); + $data['netban_ipv4'] = $redis->Get('F2B_NETBAN_IPV4'); + $data['netban_ipv6'] = $redis->Get('F2B_NETBAN_IPV6'); $wl = $redis->hGetAll('F2B_WHITELIST'); if (is_array($wl)) { foreach ($wl as $key => $value) { @@ -50,6 +52,8 @@ function fail2ban($_action, $_data = null) { $ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']); $max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['active_int']); $retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']); + $netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']); + $netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']); } else { $_SESSION['return'] = array( @@ -60,12 +64,20 @@ function fail2ban($_action, $_data = null) { } $wl = $_data['whitelist']; $ban_time = ($ban_time < 60) ? 60 : $ban_time; + + $netban_ipv4 = ($netban_ipv4 < 8) ? 8 : $netban_ipv4; + $netban_ipv6 = ($netban_ipv6 < 8) ? 8 : $netban_ipv6; + $netban_ipv4 = ($netban_ipv4 > 32) ? 32 : $netban_ipv4; + $netban_ipv6 = ($netban_ipv6 > 128) ? 128 : $netban_ipv6; + $max_attempts = ($max_attempts < 1) ? 1 : $max_attempts; $retry_window = ($retry_window < 1) ? 1 : $retry_window; try { $redis->Set('F2B_BAN_TIME', $ban_time); $redis->Set('F2B_MAX_ATTEMPTS', $max_attempts); $redis->Set('F2B_RETRY_WINDOW', $retry_window); + $redis->Set('F2B_NETBAN_IPV4', $netban_ipv4); + $redis->Set('F2B_NETBAN_IPV6', $netban_ipv6); $redis->Del('F2B_WHITELIST'); if(!empty($wl)) { $wl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $wl)); diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index b19792e2..f7f08403 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -459,8 +459,9 @@ function user_get_alias_details($username) { while ($row = array_shift($run)) { $data['shared_aliases'] = $row['shared_aliases']; } - $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '✘') AS `direct_aliases` FROM `alias` + $stmt = $pdo->prepare("SELECT GROUP_CONCAT(`address` SEPARATOR ', ') AS `direct_aliases` FROM `alias` WHERE `goto` = :username_goto + AND `address` NOT LIKE '@%' AND `address` != :username_address"); $stmt->execute( array( @@ -477,7 +478,13 @@ function user_get_alias_details($username) { $stmt->execute(array(':username' => $username)); $run = $stmt->fetchAll(PDO::FETCH_ASSOC); while ($row = array_shift($run)) { - $data['ad_alias'] = $row['ad_alias']; + if (empty($data['direct_aliases'])) { + $data['direct_aliases'] = $row['ad_alias']; + } + else { + // Probably faster than imploding + $data['direct_aliases'] .= ', ' . $row['ad_alias']; + } } $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '✘') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` NOT LIKE '@%';"); $stmt->execute(array(':username' => $username)); diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index db9d10a5..5bd9ac58 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -213,24 +213,30 @@ function mailbox($_action, $_type, $_data = null, $attr = null) { return false; } $active = intval($_data['active']); - $delete2duplicates = intval($_data['delete2duplicates']); - $delete1 = intval($_data['delete1']); - $delete2 = intval($_data['delete2']); - $port1 = $_data['port1']; - $host1 = strtolower($_data['host1']); - $password1 = $_data['password1']; - $exclude = $_data['exclude']; - $maxage = $_data['maxage']; - $subfolder2 = $_data['subfolder2']; - $user1 = $_data['user1']; - $mins_interval = $_data['mins_interval']; - $enc1 = $_data['enc1']; + $delete2duplicates = intval($_data['delete2duplicates']); + $delete1 = intval($_data['delete1']); + $delete2 = intval($_data['delete2']); + $skipcrossduplicates = intval($_data['skipcrossduplicates']); + $automap = intval($_data['automap']); + $port1 = $_data['port1']; + $host1 = strtolower($_data['host1']); + $password1 = $_data['password1']; + $exclude = $_data['exclude']; + $maxage = $_data['maxage']; + $maxbytespersecond = $_data['maxbytespersecond']; + $subfolder2 = $_data['subfolder2']; + $user1 = $_data['user1']; + $mins_interval = $_data['mins_interval']; + $enc1 = $_data['enc1']; if (empty($subfolder2)) { $subfolder2 = ""; } if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) { $maxage = "0"; } + if (!isset($maxbytespersecond) || !filter_var($maxbytespersecond, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 125000000)))) { + $maxbytespersecond = "0"; + } if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) { $_SESSION['return'] = array( 'type' => 'danger', @@ -287,14 +293,17 @@ function mailbox($_action, $_type, $_data = null, $attr = null) { return false; } try { - $stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `active`) - VALUES (:user2, :exclude, :delete1, :delete2, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :active)"); + $stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `automap`, `skipcrossduplicates`, `maxbytespersecond`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `active`) + VALUES (:user2, :exclude, :delete1, :delete2, :automap, :skipcrossduplicates, :maxbytespersecond, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :active)"); $stmt->execute(array( ':user2' => $username, ':exclude' => $exclude, ':maxage' => $maxage, ':delete1' => $delete1, ':delete2' => $delete2, + ':automap' => $automap, + ':skipcrossduplicates' => $skipcrossduplicates, + ':maxbytespersecond' => $maxbytespersecond, ':subfolder2' => $subfolder2, ':host1' => $host1, ':authmech1' => 'PLAIN', @@ -1444,6 +1453,8 @@ function mailbox($_action, $_type, $_data = null, $attr = null) { $delete2duplicates = (isset($_data['delete2duplicates'])) ? intval($_data['delete2duplicates']) : $is_now['delete2duplicates']; $delete1 = (isset($_data['delete1'])) ? intval($_data['delete1']) : $is_now['delete1']; $delete2 = (isset($_data['delete2'])) ? intval($_data['delete2']) : $is_now['delete2']; + $automap = (isset($_data['automap'])) ? intval($_data['automap']) : $is_now['automap']; + $skipcrossduplicates = (isset($_data['skipcrossduplicates'])) ? intval($_data['skipcrossduplicates']) : $is_now['skipcrossduplicates']; $port1 = (!empty($_data['port1'])) ? $_data['port1'] : $is_now['port1']; $password1 = (!empty($_data['password1'])) ? $_data['password1'] : $is_now['password1']; $host1 = (!empty($_data['host1'])) ? $_data['host1'] : $is_now['host1']; @@ -1452,6 +1463,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) { $mins_interval = (!empty($_data['mins_interval'])) ? $_data['mins_interval'] : $is_now['mins_interval']; $exclude = (!empty($_data['exclude'])) ? $_data['exclude'] : $is_now['exclude']; $maxage = (isset($_data['maxage']) && $_data['maxage'] != "") ? intval($_data['maxage']) : $is_now['maxage']; + $maxbytespersecond = (isset($_data['maxbytespersecond']) && $_data['maxbytespersecond'] != "") ? intval($_data['maxbytespersecond']) : $is_now['maxbytespersecond']; } else { $_SESSION['return'] = array( @@ -1466,6 +1478,9 @@ function mailbox($_action, $_type, $_data = null, $attr = null) { if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) { $maxage = "0"; } + if (!isset($maxbytespersecond) || !filter_var($maxbytespersecond, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 125000000)))) { + $maxbytespersecond = "0"; + } if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) { $_SESSION['return'] = array( 'type' => 'danger', @@ -1502,14 +1517,33 @@ function mailbox($_action, $_type, $_data = null, $attr = null) { return false; } try { - $stmt = $pdo->prepare("UPDATE `imapsync` SET `delete1` = :delete1, `delete2` = :delete2, `maxage` = :maxage, `subfolder2` = :subfolder2, `exclude` = :exclude, `host1` = :host1, `last_run` = :last_run, `user1` = :user1, `password1` = :password1, `mins_interval` = :mins_interval, `port1` = :port1, `enc1` = :enc1, `delete2duplicates` = :delete2duplicates, `active` = :active - WHERE `id` = :id"); + $stmt = $pdo->prepare("UPDATE `imapsync` SET `delete1` = :delete1, + `delete2` = :delete2, + `automap` = :automap, + `skipcrossduplicates` = :skipcrossduplicates, + `maxage` = :maxage, + `maxbytespersecond` = :maxbytespersecond, + `subfolder2` = :subfolder2, + `exclude` = :exclude, + `host1` = :host1, + `last_run` = :last_run, + `user1` = :user1, + `password1` = :password1, + `mins_interval` = :mins_interval, + `port1` = :port1, + `enc1` = :enc1, + `delete2duplicates` = :delete2duplicates, + `active` = :active + WHERE `id` = :id"); $stmt->execute(array( ':delete1' => $delete1, ':delete2' => $delete2, + ':automap' => $automap, + ':skipcrossduplicates' => $skipcrossduplicates, ':id' => $id, ':exclude' => $exclude, ':maxage' => $maxage, + ':maxbytespersecond' => $maxbytespersecond, ':subfolder2' => $subfolder2, ':host1' => $host1, ':user1' => $user1, diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php index e7f4a6a1..369cc47a 100644 --- a/data/web/inc/header.inc.php +++ b/data/web/inc/header.inc.php @@ -4,6 +4,7 @@ + <?=$UI_TEXTS['title_name'];?>