Merge branch 'dev'
This commit is contained in:
		@@ -137,10 +137,11 @@ while true; do
 | 
			
		||||
		fi
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	ALL_VALIDATED="$(echo ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} ${VALIDATED_MAILCOW_HOSTNAME})"
 | 
			
		||||
  # Unique elements
 | 
			
		||||
	ALL_VALIDATED=($(echo ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} ${VALIDATED_MAILCOW_HOSTNAME} | xargs -n1 | sort -u | xargs))
 | 
			
		||||
	if [[ -z ${ALL_VALIDATED[*]} ]]; then
 | 
			
		||||
		echo "Cannot validate hostnames, skipping Let's Encrypt..."
 | 
			
		||||
		echo 0
 | 
			
		||||
		exit 0
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	ORPHANED_SAN=($(echo ${SAN_ARRAY_NOW[*]} ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} ${MAILCOW_HOSTNAME} | tr ' ' '\n' | sort | uniq -u ))
 | 
			
		||||
@@ -159,7 +160,7 @@ while true; do
 | 
			
		||||
		-f ${ACME_BASE}/acme/private/account.key \
 | 
			
		||||
		-k ${ACME_BASE}/acme/private/privkey.pem \
 | 
			
		||||
		-c ${ACME_BASE}/acme \
 | 
			
		||||
		${VALIDATED_MAILCOW_HOSTNAME} ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]}
 | 
			
		||||
		${ALL_VALIDATED[*]}
 | 
			
		||||
 | 
			
		||||
	case "$?" in
 | 
			
		||||
		0) # new certs
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,11 @@ trap "kill 0" SIGINT
 | 
			
		||||
touch /var/log/clamav/clamd.log /var/log/clamav/freshclam.log
 | 
			
		||||
chown -R clamav:clamav /var/log/clamav/
 | 
			
		||||
 | 
			
		||||
if [[ "${SKIP_CLAMD}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
 | 
			
		||||
	echo "SKIP_CLAMD=y, skipping ClamAV..."
 | 
			
		||||
	exit 0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
freshclam -d &
 | 
			
		||||
clamd &
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,17 +15,27 @@ source s_src {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
destination d_combined { file("/var/log/combined.log"); };
 | 
			
		||||
destination d_redis {
 | 
			
		||||
destination d_redis_persistent_log {
 | 
			
		||||
  redis(
 | 
			
		||||
    host("redis-mailcow")
 | 
			
		||||
    persist-name("redis1")
 | 
			
		||||
    port(6379)
 | 
			
		||||
    command("LPUSH" "DOVECOT_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
filter f_mail { facility(mail) and not filter(f_debug); };
 | 
			
		||||
destination d_redis_f2b_channel {
 | 
			
		||||
  redis(
 | 
			
		||||
    host("redis-mailcow")
 | 
			
		||||
    persist-name("redis2")
 | 
			
		||||
    port(6379)
 | 
			
		||||
    command("PUBLISH" "F2B_CHANNEL" "$MESSAGE")
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
filter f_mail { facility(mail); };
 | 
			
		||||
log {
 | 
			
		||||
  source(s_src);
 | 
			
		||||
  destination(d_combined);
 | 
			
		||||
  filter(f_mail);
 | 
			
		||||
  destination(d_redis);
 | 
			
		||||
  destination(d_redis_persistent_log);
 | 
			
		||||
  destination(d_redis_f2b_channel);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ FROM python:2-alpine
 | 
			
		||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
 | 
			
		||||
 | 
			
		||||
RUN apk add -U --no-cache iptables ip6tables
 | 
			
		||||
RUN pip install docker redis
 | 
			
		||||
RUN pip install redis ipaddress
 | 
			
		||||
 | 
			
		||||
COPY logwatch.py /
 | 
			
		||||
CMD ["python2", "-u", "/logwatch.py"]
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ import signal
 | 
			
		||||
import ipaddress
 | 
			
		||||
import subprocess
 | 
			
		||||
from threading import Thread
 | 
			
		||||
import docker
 | 
			
		||||
import redis
 | 
			
		||||
import time
 | 
			
		||||
import json
 | 
			
		||||
@@ -19,33 +18,16 @@ if re.search(yes_regex, os.getenv('SKIP_FAIL2BAN', 0)):
 | 
			
		||||
	raise SystemExit
 | 
			
		||||
 | 
			
		||||
r = redis.StrictRedis(host='172.22.1.249', decode_responses=True, port=6379, db=0)
 | 
			
		||||
client = docker.from_env()
 | 
			
		||||
 | 
			
		||||
for container in client.containers.list():
 | 
			
		||||
	if "postfix-mailcow" in container.name:
 | 
			
		||||
		postfix_container = container.name
 | 
			
		||||
	elif "dovecot-mailcow" in container.name:
 | 
			
		||||
		dovecot_container = container.name
 | 
			
		||||
	elif "sogo-mailcow" in container.name:
 | 
			
		||||
		sogo_container = container.name
 | 
			
		||||
	elif "php-fpm-mailcow" in container.name:
 | 
			
		||||
		php_fpm_container = container.name
 | 
			
		||||
pubsub = r.pubsub()
 | 
			
		||||
 | 
			
		||||
RULES = {}
 | 
			
		||||
 | 
			
		||||
RULES[postfix_container] = {}
 | 
			
		||||
RULES[dovecot_container] = {}
 | 
			
		||||
RULES[sogo_container] = {}
 | 
			
		||||
RULES[php_fpm_container] = {}
 | 
			
		||||
 | 
			
		||||
RULES[postfix_container][1] = 'warning: .*\[([0-9a-f\.:]+)\]: SASL .* authentication failed'
 | 
			
		||||
RULES[dovecot_container][1] = '-login: Disconnected \(auth failed, .*\): user=.*, method=.*, rip=([0-9a-f\.:]+),'
 | 
			
		||||
RULES[dovecot_container][2] = '-login: Disconnected \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
 | 
			
		||||
RULES[dovecot_container][3] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
 | 
			
		||||
RULES[dovecot_container][4] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
 | 
			
		||||
RULES[sogo_container][1] = 'SOGo.* Login from \'([0-9a-f\.:]+)\' for user .* might not have worked'
 | 
			
		||||
RULES[php_fpm_container][1] = 'mailcow UI: Invalid password for .* by ([0-9a-f\.:]+)'
 | 
			
		||||
 | 
			
		||||
RULES[1] = 'warning: .*\[([0-9a-f\.:]+)\]: SASL .+ authentication failed'
 | 
			
		||||
RULES[2] = '-login: Disconnected \(auth failed, .+\): user=.*, method=.+, rip=([0-9a-f\.:]+),'
 | 
			
		||||
RULES[3] = '-login: Disconnected \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
 | 
			
		||||
RULES[4] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
 | 
			
		||||
RULES[5] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
 | 
			
		||||
RULES[6] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
 | 
			
		||||
RULES[7] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)'
 | 
			
		||||
 | 
			
		||||
r.setnx("F2B_BAN_TIME", "1800")
 | 
			
		||||
r.setnx("F2B_MAX_ATTEMPTS", "10")
 | 
			
		||||
@@ -149,24 +131,28 @@ def clear():
 | 
			
		||||
	print "Clearing all bans"
 | 
			
		||||
	for net in bans.copy():
 | 
			
		||||
		unban(net)
 | 
			
		||||
	pubsub.unsubscribe()
 | 
			
		||||
 | 
			
		||||
def watch(container):
 | 
			
		||||
def watch():
 | 
			
		||||
	log['time'] = int(round(time.time()))
 | 
			
		||||
	log['priority'] = "info"
 | 
			
		||||
	log['message'] = "Watching %s" % container
 | 
			
		||||
	log['message'] = "Watching Redis channel F2B_CHANNEL"
 | 
			
		||||
	r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False))
 | 
			
		||||
	print "Watching", container
 | 
			
		||||
	for msg in client.containers.get(container).attach(stream=True, logs=False):
 | 
			
		||||
		for rule_id, rule_regex in RULES[container].iteritems():
 | 
			
		||||
			result = re.search(rule_regex, msg)
 | 
			
		||||
			if result:
 | 
			
		||||
				addr = result.group(1)
 | 
			
		||||
				print "%s matched rule id %d in %s" % (addr, rule_id, container)
 | 
			
		||||
				log['time'] = int(round(time.time()))
 | 
			
		||||
				log['priority'] = "warn"
 | 
			
		||||
				log['message'] = "%s matched rule id %d in %s" % (addr, rule_id, container)
 | 
			
		||||
				r.lpush("F2B_LOG", json.dumps(log, ensure_ascii=False))
 | 
			
		||||
				ban(addr)
 | 
			
		||||
	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():
 | 
			
		||||
				if item['data'] and item['type'] == 'message':
 | 
			
		||||
					result = re.search(rule_regex, item['data'])
 | 
			
		||||
					if result:
 | 
			
		||||
						addr = result.group(1)
 | 
			
		||||
						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))
 | 
			
		||||
						ban(addr)
 | 
			
		||||
 | 
			
		||||
def autopurge():
 | 
			
		||||
	while not quit_now:
 | 
			
		||||
@@ -180,14 +166,13 @@ def autopurge():
 | 
			
		||||
			if bans[net]['attempts'] >= MAX_ATTEMPTS:
 | 
			
		||||
				if time.time() - bans[net]['last_attempt'] > BAN_TIME:
 | 
			
		||||
					unban(net)
 | 
			
		||||
		time.sleep(30)
 | 
			
		||||
		time.sleep(10)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
	threads = []
 | 
			
		||||
	for container in RULES:
 | 
			
		||||
		threads.append(Thread(target=watch, args=(container,)))
 | 
			
		||||
		threads[-1].daemon = True
 | 
			
		||||
		threads[-1].start()
 | 
			
		||||
 | 
			
		||||
	watch_thread = Thread(target=watch)
 | 
			
		||||
	watch_thread.daemon = True
 | 
			
		||||
	watch_thread.start()
 | 
			
		||||
 | 
			
		||||
	autopurge_thread = Thread(target=autopurge)
 | 
			
		||||
	autopurge_thread.daemon = True
 | 
			
		||||
@@ -197,9 +182,4 @@ if __name__ == '__main__':
 | 
			
		||||
	atexit.register(clear)
 | 
			
		||||
 | 
			
		||||
	while not quit_now:
 | 
			
		||||
		for thread in threads:
 | 
			
		||||
			if not thread.isAlive():
 | 
			
		||||
				break
 | 
			
		||||
		time.sleep(0.1)
 | 
			
		||||
	
 | 
			
		||||
	clear()
 | 
			
		||||
		time.sleep(0.5)
 | 
			
		||||
 
 | 
			
		||||
@@ -15,17 +15,27 @@ source s_src {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
destination d_combined { file("/var/log/combined.log"); };
 | 
			
		||||
destination d_redis {
 | 
			
		||||
destination d_redis_persistent_log {
 | 
			
		||||
  redis(
 | 
			
		||||
    host("redis-mailcow")
 | 
			
		||||
    persist-name("redis1")
 | 
			
		||||
    port(6379)
 | 
			
		||||
    command("LPUSH" "POSTFIX_MAILLOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
 | 
			
		||||
    command("LPUSH" "POSTFIX_LOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
filter f_mail { facility(mail) and not filter(f_debug); };
 | 
			
		||||
destination d_redis_f2b_channel {
 | 
			
		||||
  redis(
 | 
			
		||||
    host("redis-mailcow")
 | 
			
		||||
    persist-name("redis2")
 | 
			
		||||
    port(6379)
 | 
			
		||||
    command("PUBLISH" "F2B_CHANNEL" "$MESSAGE")
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
filter f_mail { facility(mail); };
 | 
			
		||||
log {
 | 
			
		||||
  source(s_src);
 | 
			
		||||
  destination(d_combined);
 | 
			
		||||
  filter(f_mail);
 | 
			
		||||
  destination(d_redis);
 | 
			
		||||
  destination(d_redis_persistent_log);
 | 
			
		||||
  destination(d_redis_f2b_channel);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
FROM debian:jessie-slim
 | 
			
		||||
FROM debian:stretch-slim
 | 
			
		||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
 | 
			
		||||
 | 
			
		||||
ARG DEBIAN_FRONTEND=noninteractive
 | 
			
		||||
@@ -11,30 +11,23 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
 | 
			
		||||
		ca-certificates \
 | 
			
		||||
		cron \
 | 
			
		||||
		gnupg \
 | 
			
		||||
		make \
 | 
			
		||||
		mysql-client \
 | 
			
		||||
		supervisor \
 | 
			
		||||
		syslog-ng \
 | 
			
		||||
		syslog-ng-core \
 | 
			
		||||
		syslog-ng-mod-redis \
 | 
			
		||||
		tar \
 | 
			
		||||
		dirmngr \
 | 
			
		||||
		wget \
 | 
			
		||||
		zip \
 | 
			
		||||
	&& rm -rf /var/lib/apt/lists/* \
 | 
			
		||||
	&& dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
 | 
			
		||||
	&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
 | 
			
		||||
	&& wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc" \
 | 
			
		||||
	&& export GNUPGHOME="$(mktemp -d)" \
 | 
			
		||||
	&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
 | 
			
		||||
	&& gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
 | 
			
		||||
	&& rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \
 | 
			
		||||
	&& chmod +x /usr/local/bin/gosu \
 | 
			
		||||
	&& gosu nobody true
 | 
			
		||||
 | 
			
		||||
RUN mkdir /usr/share/doc/sogo \
 | 
			
		||||
	&& touch /usr/share/doc/sogo/empty.sh \
 | 
			
		||||
	&& apt-key adv --keyserver keys.gnupg.net --recv-key 0x810273C4 \
 | 
			
		||||
	&& echo "deb http://packages.inverse.ca/SOGo/nightly/3/debian/ jessie jessie" > /etc/apt/sources.list.d/sogo.list \
 | 
			
		||||
	&& apt-key adv --keyserver sks.labs.nic.cz --recv-key A04BE668 \
 | 
			
		||||
	&& echo "deb http://www.axis.cz/linux/debian stretch sogo-v3" > /etc/apt/sources.list.d/sogo.list \
 | 
			
		||||
	&& apt-get update && apt-get install -y --force-yes \
 | 
			
		||||
		sogo \
 | 
			
		||||
		sogo-activesync \
 | 
			
		||||
 
 | 
			
		||||
@@ -93,9 +93,6 @@ echo '    </dict>
 | 
			
		||||
chown sogo:sogo -R /var/lib/sogo/
 | 
			
		||||
chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
 | 
			
		||||
 | 
			
		||||
# Regenerate the SOGo Integrator plugin
 | 
			
		||||
/thunderbird/build-plugins.sh ${MAILCOW_HOSTNAME} < <(mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain;" -B -N)
 | 
			
		||||
 | 
			
		||||
supervisorctl restart sogo
 | 
			
		||||
 | 
			
		||||
sleep 99999
 | 
			
		||||
 
 | 
			
		||||
@@ -19,13 +19,22 @@ source s_sogo {
 | 
			
		||||
destination d_combined {
 | 
			
		||||
  file("/var/log/combined.log");
 | 
			
		||||
};
 | 
			
		||||
destination d_redis {
 | 
			
		||||
destination d_redis_persistent_log {
 | 
			
		||||
  redis(
 | 
			
		||||
    host("redis-mailcow")
 | 
			
		||||
    persist-name("redis1")
 | 
			
		||||
    port(6379)
 | 
			
		||||
    command("LPUSH" "SOGO_LOG" "$(format-json time=\"$S_UNIXTIME\" priority=\"$PRIORITY\" program=\"$PROGRAM\" message=\"$MESSAGE\")\n")
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
destination d_redis_f2b_channel {
 | 
			
		||||
  redis(
 | 
			
		||||
    host("redis-mailcow")
 | 
			
		||||
    persist-name("redis2")
 | 
			
		||||
    port(6379)
 | 
			
		||||
    command("PUBLISH" "F2B_CHANNEL" "$MESSAGE")
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
log {
 | 
			
		||||
  source(s_sogo);
 | 
			
		||||
  source(s_src);
 | 
			
		||||
@@ -33,5 +42,6 @@ log {
 | 
			
		||||
};
 | 
			
		||||
log {
 | 
			
		||||
  source(s_sogo);
 | 
			
		||||
  destination(d_redis);
 | 
			
		||||
  destination(d_redis_persistent_log);
 | 
			
		||||
  destination(d_redis_f2b_channel);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -231,7 +231,7 @@ $tfa_data = get_tfa();
 | 
			
		||||
          <button class="btn btn-default" id="add_item" data-id="dkim" data-api-url='add/dkim' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
 | 
			
		||||
        </form>
 | 
			
		||||
 | 
			
		||||
        <legend data-target="#import_dkim" style="margin-top:40px;cursor:pointer" data-toggle="collapse">↳ <?=$lang['admin']['import_private_key'];?></legend>
 | 
			
		||||
        <legend data-target="#import_dkim" style="margin-top:40px;cursor:pointer" id="import_dkim_legend" unselectable="on" data-toggle="collapse"><span id="import_dkim_arrow" class="rotate glyphicon glyphicon-menu-down"></span> <?=$lang['admin']['import_private_key'];?></legend>
 | 
			
		||||
        <div id="import_dkim" class="collapse">
 | 
			
		||||
        <form class="form" data-id="dkim_import" role="form" method="post">
 | 
			
		||||
          <div class="form-group">
 | 
			
		||||
 
 | 
			
		||||
@@ -31,3 +31,14 @@ body.modal-open {
 | 
			
		||||
.inputMissingAttr {
 | 
			
		||||
  border-color: #FF4136;
 | 
			
		||||
}
 | 
			
		||||
.rotate {
 | 
			
		||||
  -moz-transition: all 0.3s linear;
 | 
			
		||||
  -webkit-transition: all 0.3s linear;
 | 
			
		||||
  transition: all 0.3s linear;
 | 
			
		||||
}
 | 
			
		||||
.rotate.animation {
 | 
			
		||||
  -ms-transform:rotateX(180deg);
 | 
			
		||||
  -moz-transform:rotateX(180deg);
 | 
			
		||||
  -webkit-transform:rotateX(180deg);
 | 
			
		||||
  transform:rotateX(180deg);
 | 
			
		||||
}
 | 
			
		||||
@@ -91,4 +91,11 @@ body.modal-open {
 | 
			
		||||
  max-width: 550px;
 | 
			
		||||
  z-index: 2000;
 | 
			
		||||
}
 | 
			
		||||
.input-group-sm .btn { margin-top: 0px !important }
 | 
			
		||||
.input-group-sm .btn { margin-top: 0px !important }
 | 
			
		||||
legend {
 | 
			
		||||
  -webkit-user-select: none;
 | 
			
		||||
  -moz-user-select: none;
 | 
			
		||||
  -ms-user-select: none
 | 
			
		||||
  -o-user-select: none;
 | 
			
		||||
  user-select: none;
 | 
			
		||||
}
 | 
			
		||||
@@ -168,6 +168,7 @@ function doveadm_authenticate($hash, $algorithm, $password) {
 | 
			
		||||
}
 | 
			
		||||
function check_login($user, $pass) {
 | 
			
		||||
	global $pdo;
 | 
			
		||||
	global $redis;
 | 
			
		||||
	if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
@@ -229,10 +230,12 @@ function check_login($user, $pass) {
 | 
			
		||||
	}
 | 
			
		||||
	if (!isset($_SESSION['ldelay'])) {
 | 
			
		||||
		$_SESSION['ldelay'] = "0";
 | 
			
		||||
    $redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
 | 
			
		||||
    error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
 | 
			
		||||
	}
 | 
			
		||||
	elseif (!isset($_SESSION['mailcow_cc_username'])) {
 | 
			
		||||
		$_SESSION['ldelay'] = $_SESSION['ldelay']+0.5;
 | 
			
		||||
    $redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
 | 
			
		||||
		error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
 | 
			
		||||
	}
 | 
			
		||||
	sleep($_SESSION['ldelay']);
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,10 @@ jQuery(function($){
 | 
			
		||||
    e.preventDefault();
 | 
			
		||||
    draw_rspamd_history();
 | 
			
		||||
  });
 | 
			
		||||
  $("#import_dkim_legend").on('click', function(e) {
 | 
			
		||||
    e.preventDefault();
 | 
			
		||||
    $('#import_dkim_arrow').toggleClass("animation"); 
 | 
			
		||||
  });
 | 
			
		||||
  function draw_postfix_logs() {
 | 
			
		||||
    ft_postfix_logs = FooTable.init('#postfix_log', {
 | 
			
		||||
      "columns": [
 | 
			
		||||
 
 | 
			
		||||
@@ -1,96 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
/* updates.php - this file is part of SOGo
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2006-2014 Inverse inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; see the file COPYING.  If not, write to
 | 
			
		||||
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 | 
			
		||||
 * Boston, MA 02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* This script handles the automatic propagation of extensions pertaining to a
 | 
			
		||||
   SOGo site. It requires PHP 4.1.0 or later. */
 | 
			
		||||
$plugin_dir = 'thunderbird-plugins';
 | 
			
		||||
chdir($plugin_dir);
 | 
			
		||||
$plugins = array();
 | 
			
		||||
 | 
			
		||||
if (file_exists('version.csv'))
 | 
			
		||||
{
 | 
			
		||||
  $fh = fopen('version.csv', 'r');
 | 
			
		||||
  if ($fh)
 | 
			
		||||
  {
 | 
			
		||||
    while (($row = fgetcsv($fh, 1000, ';')) !== FALSE)
 | 
			
		||||
    {
 | 
			
		||||
      $plugins[$row[0]] = array(
 | 
			
		||||
        'application' => 'thunderbird',
 | 
			
		||||
        'version' => $row[1],
 | 
			
		||||
        'filename' => str_replace('__DOMAIN__', $_GET["domain"], $row[2]),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    fclose($fh);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$applications
 | 
			
		||||
= array( "thunderbird" => "<em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
 | 
			
		||||
                <em:minVersion>31.0</em:minVersion>
 | 
			
		||||
                <em:maxVersion>31.*</em:maxVersion>" );
 | 
			
		||||
 | 
			
		||||
$pluginname = $_GET["plugin"];
 | 
			
		||||
$plugin =& $plugins[$pluginname];
 | 
			
		||||
$application =& $applications[$plugin["application"]];
 | 
			
		||||
 | 
			
		||||
if ( $plugin ) {
 | 
			
		||||
  $platform = $_GET["platform"];
 | 
			
		||||
  if ( $platform
 | 
			
		||||
       && file_exists( $platform . "/" . $plugin["filename"] ) ) {
 | 
			
		||||
    $plugin["filename"] = $platform . "/" . $plugin["filename"];
 | 
			
		||||
  }
 | 
			
		||||
  elseif ( !file_exists( $plugin["filename"] ) ) {
 | 
			
		||||
    $plugin = false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if ( $plugin ) {
 | 
			
		||||
  header("Content-type: text/xml; charset=utf-8");
 | 
			
		||||
  echo ('<?xml version="1.0"?>' . "\n");
 | 
			
		||||
?>
 | 
			
		||||
<!DOCTYPE RDF>
 | 
			
		||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
  xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 | 
			
		||||
  <Description about="urn:mozilla:extension:<?php echo $pluginname ?>">
 | 
			
		||||
    <em:updates>
 | 
			
		||||
      <Seq>
 | 
			
		||||
        <li>
 | 
			
		||||
          <Description>
 | 
			
		||||
            <em:version><?php echo $plugin["version"] ?></em:version>
 | 
			
		||||
            <em:targetApplication>
 | 
			
		||||
              <Description>
 | 
			
		||||
                <?php echo $applications[$plugin["application"]] ?>
 | 
			
		||||
                
 | 
			
		||||
                <em:updateLink><?php echo 'https://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . '/' .  $plugin_dir . '/' . $plugin["filename"] ?></em:updateLink>
 | 
			
		||||
              </Description>
 | 
			
		||||
            </em:targetApplication>
 | 
			
		||||
          </Description>
 | 
			
		||||
        </li>
 | 
			
		||||
      </Seq>
 | 
			
		||||
    </em:updates>
 | 
			
		||||
  </Description>
 | 
			
		||||
</RDF>
 | 
			
		||||
<?php
 | 
			
		||||
} else {
 | 
			
		||||
  header("Content-type: text/plain; charset=utf-8", true, 404);
 | 
			
		||||
  echo( 'Plugin not found' );
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
							
								
								
									
										5
									
								
								data/web/thunderbird-plugins/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								data/web/thunderbird-plugins/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +0,0 @@
 | 
			
		||||
*.zip
 | 
			
		||||
sogo-*-master
 | 
			
		||||
version.csv
 | 
			
		||||
*.xpi
 | 
			
		||||
*.tar.gz
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
MAILHOST=$1
 | 
			
		||||
 | 
			
		||||
cd $(dirname $0)
 | 
			
		||||
 | 
			
		||||
wget -O integrator.tar.gz https://github.com/inverse-inc/sogo-integrator.tb31/archive/master.tar.gz
 | 
			
		||||
wget -O connector.tar.gz https://github.com/inverse-inc/sogo-connector.tb31/archive/master.tar.gz
 | 
			
		||||
 | 
			
		||||
mkdir -p integrator connector
 | 
			
		||||
tar --strip-components=1 -C integrator -xf integrator.tar.gz
 | 
			
		||||
tar --strip-components=1 -C connector -xf connector.tar.gz
 | 
			
		||||
 | 
			
		||||
# build custom integrator
 | 
			
		||||
while read DOMAIN; do
 | 
			
		||||
	echo "Building SOGo Integrator for $DOMAIN hosted on $MAILHOST"
 | 
			
		||||
	cd integrator
 | 
			
		||||
	echo > defaults/preferences/site.js
 | 
			
		||||
	mkdir -p custom/${DOMAIN}
 | 
			
		||||
	cp -r custom/sogo-demo/* custom/${DOMAIN}/
 | 
			
		||||
	sed -i "s/http:\/\/sogo-demo\.inverse\.ca/https:\/\/${MAILHOST}/g" custom/${DOMAIN}/chrome/content/extensions.rdf
 | 
			
		||||
	sed -i "s/plugins\/updates\.php[?]/thunderbird-plugins.php?domain=${DOMAIN}\&/g" custom/${DOMAIN}/chrome/content/extensions.rdf
 | 
			
		||||
	echo 'pref("sogo-integrator.autocomplete.server.urlid", "'${DOMAIN}'");' > custom/${DOMAIN}/defaults/preferences/site.js
 | 
			
		||||
	echo 'pref("mail.collect_email_address_outgoing", false);' >> custom/${DOMAIN}/defaults/preferences/site.js
 | 
			
		||||
	sed -i 's/<\/Seq>/<li><Description em:id="sieve@mozdev.org" em:name="Sieve"\/><\/li><li><Description em:id="imap-acl@sirphreak.com" em:name="Imap-ACL-Extension"\/><\/li><\/Seq>/g' custom/${DOMAIN}/chrome/content/extensions.rdf
 | 
			
		||||
	make build=${DOMAIN}
 | 
			
		||||
	INTEGRATOR_VER=$(grep em:version install.rdf | awk -F '"' '{print $2}')
 | 
			
		||||
	cp sogo-integrator-*-${DOMAIN}.xpi ../sogo-integrator-${INTEGRATOR_VER}-${DOMAIN}.xpi
 | 
			
		||||
	cd ..
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# build connector
 | 
			
		||||
cd connector
 | 
			
		||||
make
 | 
			
		||||
CONNECTOR_VER=$(grep em:version install.rdf | awk -F '"' '{print $2}')
 | 
			
		||||
cp sogo-connector-*.xpi ../sogo-connector-${CONNECTOR_VER}.xpi
 | 
			
		||||
cd ..
 | 
			
		||||
 | 
			
		||||
# download Sieve plugin
 | 
			
		||||
SIEVE_RELEASES=$(wget --header="Accept: application/vnd.github.v3+json" -qO - https://api.github.com/repos/thsmi/sieve/releases)
 | 
			
		||||
SIEVE_VER=$(echo "$SIEVE_RELEASES" | grep -o '"tag_name": *"[^"]*"' | head -n 1 | awk -F '"' '{print $4}')
 | 
			
		||||
SIEVE_URL=$(echo "$SIEVE_RELEASES" | grep -o '"browser_download_url": *"[^"]*"' | head -n 1 | awk -F '"' '{print $4}')
 | 
			
		||||
wget -O sieve-${SIEVE_VER}.xpi ${SIEVE_URL}
 | 
			
		||||
unset SIEVE_RELEASES
 | 
			
		||||
 | 
			
		||||
# download ACL plugin
 | 
			
		||||
IMAP_ACL_RELEASES=$(wget -qO - 'https://addons.mozilla.org/api/v3/addons/addon/176736')
 | 
			
		||||
IMAP_ACL_VER=$(echo "$IMAP_ACL_RELEASES" | grep -o '"version": *"[^"]*"' | head -n 1 | awk -F '"' '{print $4}')
 | 
			
		||||
IMAP_ACL_URL=$(echo "$IMAP_ACL_RELEASES" | grep -o '"url": *"[^"]*\.xpi' | head -n 1 | awk -F '"' '{print $4}')
 | 
			
		||||
wget -O imap_acl_extension-${IMAP_ACL_VER}-tb.xpi ${IMAP_ACL_URL}
 | 
			
		||||
unset IMAP_ACL_RELEASES
 | 
			
		||||
 | 
			
		||||
# update version file
 | 
			
		||||
echo "sogo-connector@inverse.ca;${CONNECTOR_VER};sogo-connector-${CONNECTOR_VER}.xpi" > version.csv
 | 
			
		||||
echo "sogo-integrator@inverse.ca;${INTEGRATOR_VER};sogo-integrator-${INTEGRATOR_VER}-__DOMAIN__.xpi" >> version.csv
 | 
			
		||||
echo "sieve@mozdev.org;${SIEVE_VER};sieve-${SIEVE_VER}.xpi" >> version.csv
 | 
			
		||||
echo "imap-acl@sirphreak.com;${IMAP_ACL_VER};imap_acl_extension-${IMAP_ACL_VER}-tb.xpi" >> version.csv
 | 
			
		||||
 | 
			
		||||
rm -rf connector integrator *.tar.gz
 | 
			
		||||
@@ -66,9 +66,11 @@ services:
 | 
			
		||||
            - redis
 | 
			
		||||
 | 
			
		||||
    clamd-mailcow:
 | 
			
		||||
      image: mailcow/clamd:1.0
 | 
			
		||||
      image: mailcow/clamd:1.1
 | 
			
		||||
      build: ./data/Dockerfiles/clamd
 | 
			
		||||
      restart: always
 | 
			
		||||
      environment:
 | 
			
		||||
        - SKIP_CLAMD=${SKIP_CLAMD:-n}
 | 
			
		||||
      dns:
 | 
			
		||||
        - 172.22.1.254
 | 
			
		||||
      dns_search: mailcow-network
 | 
			
		||||
@@ -143,7 +145,7 @@ services:
 | 
			
		||||
            - phpfpm
 | 
			
		||||
 | 
			
		||||
    sogo-mailcow:
 | 
			
		||||
      image: mailcow/sogo:1.0
 | 
			
		||||
      image: mailcow/sogo:1.1
 | 
			
		||||
      build: ./data/Dockerfiles/sogo
 | 
			
		||||
      depends_on:
 | 
			
		||||
        unbound-mailcow:
 | 
			
		||||
@@ -156,7 +158,6 @@ services:
 | 
			
		||||
        - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
 | 
			
		||||
      volumes:
 | 
			
		||||
        - ./data/conf/sogo/:/etc/sogo/
 | 
			
		||||
        - ./data/web/thunderbird-plugins:/thunderbird
 | 
			
		||||
      restart: always
 | 
			
		||||
      logging:
 | 
			
		||||
        options:
 | 
			
		||||
@@ -171,7 +172,7 @@ services:
 | 
			
		||||
            - sogo
 | 
			
		||||
 | 
			
		||||
    dovecot-mailcow:
 | 
			
		||||
      image: mailcow/dovecot:1.1
 | 
			
		||||
      image: mailcow/dovecot:1.2
 | 
			
		||||
      build: ./data/Dockerfiles/dovecot
 | 
			
		||||
      depends_on:
 | 
			
		||||
        unbound-mailcow:
 | 
			
		||||
@@ -206,7 +207,7 @@ services:
 | 
			
		||||
            - dovecot
 | 
			
		||||
 | 
			
		||||
    postfix-mailcow:
 | 
			
		||||
      image: mailcow/postfix:1.0
 | 
			
		||||
      image: mailcow/postfix:1.1
 | 
			
		||||
      build: ./data/Dockerfiles/postfix
 | 
			
		||||
      depends_on:
 | 
			
		||||
        unbound-mailcow:
 | 
			
		||||
@@ -293,7 +294,7 @@ services:
 | 
			
		||||
    acme-mailcow:
 | 
			
		||||
      depends_on:
 | 
			
		||||
        - nginx-mailcow
 | 
			
		||||
      image: mailcow/acme:1.11
 | 
			
		||||
      image: mailcow/acme:1.12
 | 
			
		||||
      build: ./data/Dockerfiles/acme
 | 
			
		||||
      dns:
 | 
			
		||||
        - 172.22.1.254
 | 
			
		||||
@@ -319,7 +320,7 @@ services:
 | 
			
		||||
            - acme
 | 
			
		||||
 | 
			
		||||
    fail2ban-mailcow:
 | 
			
		||||
      image: mailcow/fail2ban:1.4
 | 
			
		||||
      image: mailcow/fail2ban:1.5
 | 
			
		||||
      build: ./data/Dockerfiles/fail2ban
 | 
			
		||||
      depends_on:
 | 
			
		||||
        - dovecot-mailcow
 | 
			
		||||
@@ -337,8 +338,8 @@ services:
 | 
			
		||||
        - 172.22.1.254
 | 
			
		||||
      dns_search: mailcow-network
 | 
			
		||||
      volumes:
 | 
			
		||||
        - /var/run/docker.sock:/var/run/docker.sock:ro
 | 
			
		||||
        - /lib/modules:/lib/modules:ro
 | 
			
		||||
 | 
			
		||||
    ipv6nat:
 | 
			
		||||
      image: robbertkl/ipv6nat
 | 
			
		||||
      restart: always
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,7 @@ HTTPS_BIND=0.0.0.0
 | 
			
		||||
# ------------------------------
 | 
			
		||||
# You should leave that alone
 | 
			
		||||
# Format: 11.22.33.44:25 or 0.0.0.0:465 etc.
 | 
			
		||||
# Do _not_ use IP:PORT in HTTPS_BIND or HTTPS_PORT
 | 
			
		||||
# Do _not_ use IP:PORT in HTTP(S)_BIND or HTTP(S)_PORT
 | 
			
		||||
 | 
			
		||||
SMTP_PORT=25
 | 
			
		||||
SMTPS_PORT=465
 | 
			
		||||
@@ -87,6 +87,9 @@ SKIP_IP_CHECK=n
 | 
			
		||||
# To never run fail2ban-mailcow
 | 
			
		||||
SKIP_FAIL2BAN=n
 | 
			
		||||
 | 
			
		||||
# To never run clamd-mailcow
 | 
			
		||||
SKIP_CLAMD=n
 | 
			
		||||
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
mkdir -p data/assets/ssl
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								update.sh
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								update.sh
									
									
									
									
									
								
							@@ -7,6 +7,25 @@ if [[ -z $(which git) ]]; then echo "Cannot find git, exiting."; exit 1; fi
 | 
			
		||||
if [[ -z $(which awk) ]]; then echo "Cannot find awk, exiting."; exit 1; fi
 | 
			
		||||
if [[ -z $(which sha1sum) ]]; then echo "Cannot find sha1sum, exiting."; exit 1; fi
 | 
			
		||||
 | 
			
		||||
CONFIG_ARRAY=("SKIP_LETS_ENCRYPT" "SKIP_CLAMD" "SKIP_IP_CHECK" "SKIP_FAIL2BAN" "ADDITIONAL_SAN")
 | 
			
		||||
echo >> mailcow.conf
 | 
			
		||||
for option in ${CONFIG_ARRAY[@]}; do
 | 
			
		||||
	if [[ ${option} == "ADDITIONAL_SAN" ]]; then
 | 
			
		||||
		if ! grep -q ${option} mailcow.conf; then
 | 
			
		||||
			echo "Adding new option \"${option}\" to mailcow.conf"
 | 
			
		||||
			echo "${option}=" >> mailcow.conf
 | 
			
		||||
		fi
 | 
			
		||||
	elif [[ ${option} == "COMPOSE_PROJECT_NAME" ]]; then
 | 
			
		||||
		if ! grep -q ${option} mailcow.conf; then
 | 
			
		||||
			echo "Adding new option \"${option}\" to mailcow.conf"
 | 
			
		||||
			echo "${COMPOSE_PROJECT_NAME}=mailcow-dockerized" >> mailcow.conf
 | 
			
		||||
		fi
 | 
			
		||||
	elif ! grep -q ${option} mailcow.conf; then
 | 
			
		||||
		echo "Adding new option \"${option}\" to mailcow.conf"
 | 
			
		||||
		echo "${option}=n" >> mailcow.conf
 | 
			
		||||
	fi
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
echo -en "Checking internet connection... "
 | 
			
		||||
curl -o /dev/null google.com -sm3
 | 
			
		||||
if [[ $? != 0 ]]; then
 | 
			
		||||
@@ -106,14 +125,16 @@ chmod +x $(which docker-compose)
 | 
			
		||||
echo -e "\e[32mStarting mailcow...\e[0m"
 | 
			
		||||
sleep 2
 | 
			
		||||
docker-compose up -d --remove-orphans
 | 
			
		||||
#echo -e "\e[32mCleaning up Docker objects...\e[0m"
 | 
			
		||||
if docker images -f "dangling=true" | grep ago --quiet; then
 | 
			
		||||
	docker rmi -f $(docker images -f "dangling=true" -q)
 | 
			
		||||
#echo -e "\e[32mCleaning up dangling Docker objects...\e[0m"
 | 
			
		||||
if [[ ! -z $(docker images -qf "dangling=true") ]]; then
 | 
			
		||||
	docker rmi -f $(docker images -qf "dangling=true" -q)
 | 
			
		||||
fi
 | 
			
		||||
if [[ ! -z $(docker volume ls -qf dangling=true) ]]; then
 | 
			
		||||
	docker volume rm $(docker volume ls -qf dangling=true)
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo "In case you encounter any problem, hard-reset to a state before updating mailcow:"
 | 
			
		||||
echo
 | 
			
		||||
git reflog --color=always | grep "Before update on "
 | 
			
		||||
echo
 | 
			
		||||
echo "Use \"git reset --hard hash-on-the-left\" and run docker-compose up -d afterwards."
 | 
			
		||||
#echo "In case you encounter any problem, hard-reset to a state before updating mailcow:"
 | 
			
		||||
#echo
 | 
			
		||||
#git reflog --color=always | grep "Before update on "
 | 
			
		||||
#echo
 | 
			
		||||
#echo "Use \"git reset --hard hash-on-the-left\" and run docker-compose up -d afterwards."
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user