1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2026-06-13 01:50:34 +00:00

Compare commits

...

28 Commits

Author SHA1 Message Date
Niklas Meyer 1e09df20b6 Merge pull request #5689 from mailcow/staging
2024-01c
2024-02-02 15:52:33 +01:00
Patrick Schult 087481ac12 Merge pull request #5696 from mailcow/fix/netfilter
[Netfilter] add mailcow isolation rule to MAILCOW chain
2024-02-02 14:33:01 +01:00
FreddleSpl0it c941e802d4 [Netfilter] only perform cleanup at exit if SIGTERM was recieved 2024-02-02 12:57:21 +01:00
FreddleSpl0it 39589bd441 [Netfilter] only perform cleanup at exit if SIGTERM was recieved 2024-02-02 12:46:50 +01:00
DerLinkman 2e57325dde docker-compose.yml: Bump dovecot + netfilter version 2024-02-02 11:27:46 +01:00
FreddleSpl0it 2072301d89 [Netfilter] only perform cleanup at exit if SIGTERM was recieved 2024-02-02 11:08:44 +01:00
FreddleSpl0it b236fd3ac6 [Netfilter] add mailcow isolation rule to MAILCOW chain
[Netfilter] add mailcow rule to docker-user chain

[Netfilter] add mailcow isolation rule to MAILCOW chain

[Netfilter] add mailcow isolation rule to MAILCOW chain

[Netfilter] set mailcow isolation rule before redis

[Netfilter] clear bans in redis after connecting

[Netfilter] simplify mailcow isolation rule for compatibility with iptables-nft

[Netfilter] stop container after mariadb, redis, dovecot, solr

[Netfilter] simplify mailcow isolation rule for compatibility with iptables-nft

[Netfilter] add exception for mailcow isolation rule for HA setups

[Netfilter] add exception for mailcow isolation rule for HA setups

[Netfilter] add DISABLE_NETFILTER_ISOLATION_RULE

[Netfilter] fix wrong var name

[Netfilter] add DISABLE_NETFILTER_ISOLATION_RULE to update and generate_config sh
2024-02-02 10:10:11 +01:00
Niklas Meyer b968695e31 Merge pull request #5686 from mailcow/update/postscreen_access.cidr
[Postfix] update postscreen_access.cidr
2024-02-01 08:58:35 +01:00
Niklas Meyer 694f1d1623 Merge pull request #5688 from mailcow/fix/sogo-authenticated-users
sogo: fix ACL allow authenticated users + rebuild on Bookworm
2024-02-01 08:42:53 +01:00
DerLinkman 93e4d58606 sogo: fix ACL allow authenticated users + rebuild on Bookworm 2024-02-01 08:41:11 +01:00
milkmaker cc77caad67 update postscreen_access.cidr 2024-02-01 00:13:56 +00:00
renovate[bot] f74573f5d0 chore(deps): update peter-evans/create-pull-request action to v6 (#5683)
Signed-off-by: milkmaker <milkmaker@mailcow.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-31 16:14:42 +01:00
DerLinkman deb6f0babc issue: added architecture as dropdown 2024-01-23 08:46:06 +01:00
Niklas Meyer cb978136bd Merge pull request #5663 from mailcow/staging
2024-01b
2024-01-22 11:50:41 +01:00
Niklas Meyer 1159450cc4 Merge pull request #5662 from mailcow/fix/rollback-curl-bug
fix: rollback curl bug
2024-01-22 11:39:27 +01:00
DerLinkman a0613e4b10 fix: rollback of Alpine 3.19 were possible 2024-01-22 11:26:26 +01:00
Niklas Meyer 68989f0a45 Merge pull request #5647 from Candinya/patch-1
fix: watchdog webhook body variables injector
2024-01-22 10:34:06 +01:00
DerLinkman 7da5e3697e compose: bump watchdog version 2024-01-22 10:32:01 +01:00
Nya Candy 6e7a0eb662 fix: watchdog webhook body variables injector 2024-01-22 10:32:01 +01:00
Niklas Meyer b25ac855ca Merge pull request #5660 from luminem/openrc-support
Test for openrc configuration file instead of alpine
2024-01-22 10:27:29 +01:00
Niklas Meyer 3e02dcbb95 Merge pull request #5652 from KagurazakaNyaa/master
Allow user skip unbound healthcheck
2024-01-22 10:25:50 +01:00
DerLinkman 53be119e39 compose: bump unbound version 2024-01-22 10:22:24 +01:00
Luca Barbato 25bdc4c9ed Test for openrc configuration file instead of alpine
This way other distro using openrc can be supported.
2024-01-22 09:50:24 +01:00
KagurazakaNyaa 9d4055fc4d add parameter SKIP_UNBOUND_HEALTHCHECK to old installations 2024-01-19 00:07:51 +08:00
KagurazakaNyaa d2edf359ac update config comment 2024-01-18 23:53:08 +08:00
KagurazakaNyaa aa1d92dfbb add SKIP_UNBOUND_HEALTHCHECK to docker-compose.yml 2024-01-18 23:50:26 +08:00
KagurazakaNyaa b89d71e6e4 change variable name 2024-01-18 23:48:59 +08:00
KagurazakaNyaa ed493f9c3a Allow user skip unbound healthcheck 2024-01-18 23:28:03 +08:00
20 changed files with 422 additions and 96 deletions
+10
View File
@@ -62,6 +62,16 @@ body:
- nightly - nightly
validations: validations:
required: true required: true
- type: dropdown
attributes:
label: "Which architecture are you using?"
description: "#### `uname -m`"
multiple: false
options:
- x86
- ARM64 (aarch64)
validations:
required: true
- type: input - type: input
attributes: attributes:
label: "Operating System:" label: "Operating System:"
@@ -22,7 +22,7 @@ jobs:
bash helper-scripts/update_postscreen_whitelist.sh bash helper-scripts/update_postscreen_whitelist.sh
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v5 uses: peter-evans/create-pull-request@v6
with: with:
token: ${{ secrets.mailcow_action_Update_postscreen_access_cidr_pat }} token: ${{ secrets.mailcow_action_Update_postscreen_access_cidr_pat }}
commit-message: update postscreen_access.cidr commit-message: update postscreen_access.cidr
+1
View File
@@ -13,6 +13,7 @@ data/conf/dovecot/acl_anyone
data/conf/dovecot/dovecot-master.passwd data/conf/dovecot/dovecot-master.passwd
data/conf/dovecot/dovecot-master.userdb data/conf/dovecot/dovecot-master.userdb
data/conf/dovecot/extra.conf data/conf/dovecot/extra.conf
data/conf/dovecot/mail_replica.conf
data/conf/dovecot/global_sieve_* data/conf/dovecot/global_sieve_*
data/conf/dovecot/last_login data/conf/dovecot/last_login
data/conf/dovecot/lua data/conf/dovecot/lua
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.19 FROM alpine:3.18
LABEL maintainer "The Infrastructure Company GmbH GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH GmbH <info@servercow.de>"
@@ -335,6 +335,15 @@ sys.exit()
EOF EOF
fi fi
# Set mail_replica for HA setups
if [[ -n ${MAILCOW_REPLICA_IP} && -n ${DOVEADM_REPLICA_PORT} ]]; then
cat <<EOF > /etc/dovecot/mail_replica.conf
# Autogenerated by mailcow
mail_replica = tcp:${MAILCOW_REPLICA_IP}:${DOVEADM_REPLICA_PORT}
EOF
fi
# 401 is user dovecot # 401 is user dovecot
if [[ ! -s /mail_crypt/ecprivkey.pem || ! -s /mail_crypt/ecpubkey.pem ]]; then if [[ ! -s /mail_crypt/ecprivkey.pem || ! -s /mail_crypt/ecpubkey.pem ]]; then
openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem openssl ecparam -name prime256v1 -genkey | openssl pkey -out /mail_crypt/ecprivkey.pem
+74 -47
View File
@@ -21,28 +21,6 @@ from modules.IPTables import IPTables
from modules.NFTables import NFTables from modules.NFTables import NFTables
# connect to redis
while True:
try:
redis_slaveof_ip = os.getenv('REDIS_SLAVEOF_IP', '')
redis_slaveof_port = os.getenv('REDIS_SLAVEOF_PORT', '')
if "".__eq__(redis_slaveof_ip):
r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0)
else:
r = redis.StrictRedis(host=redis_slaveof_ip, decode_responses=True, port=redis_slaveof_port, db=0)
r.ping()
except Exception as ex:
print('%s - trying again in 3 seconds' % (ex))
time.sleep(3)
else:
break
pubsub = r.pubsub()
# rename fail2ban to netfilter
if r.exists('F2B_LOG'):
r.rename('F2B_LOG', 'NETFILTER_LOG')
# globals # globals
WHITELIST = [] WHITELIST = []
BLACKLIST= [] BLACKLIST= []
@@ -50,18 +28,10 @@ bans = {}
quit_now = False quit_now = False
exit_code = 0 exit_code = 0
lock = Lock() lock = Lock()
chain_name = "MAILCOW"
r = None
# init Logger pubsub = None
logger = Logger(r) clear_before_quit = False
# init backend
backend = sys.argv[1]
if backend == "nftables":
logger.logInfo('Using NFTables backend')
tables = NFTables("MAILCOW", logger)
else:
logger.logInfo('Using IPTables backend')
tables = IPTables("MAILCOW", logger)
def refreshF2boptions(): def refreshF2boptions():
@@ -250,17 +220,21 @@ def clear():
with lock: with lock:
tables.clearIPv4Table() tables.clearIPv4Table()
tables.clearIPv6Table() tables.clearIPv6Table()
r.delete('F2B_ACTIVE_BANS') try:
r.delete('F2B_PERM_BANS') if r is not None:
pubsub.unsubscribe() r.delete('F2B_ACTIVE_BANS')
r.delete('F2B_PERM_BANS')
except Exception as ex:
logger.logWarn('Error clearing redis keys F2B_ACTIVE_BANS and F2B_PERM_BANS: %s' % ex)
def watch(): def watch():
logger.logInfo('Watching Redis channel F2B_CHANNEL') global pubsub
pubsub.subscribe('F2B_CHANNEL')
global quit_now global quit_now
global exit_code global exit_code
logger.logInfo('Watching Redis channel F2B_CHANNEL')
pubsub.subscribe('F2B_CHANNEL')
while not quit_now: while not quit_now:
try: try:
for item in pubsub.listen(): for item in pubsub.listen():
@@ -280,6 +254,7 @@ def watch():
ban(addr) ban(addr)
except Exception as ex: except Exception as ex:
logger.logWarn('Error reading log line from pubsub: %s' % ex) logger.logWarn('Error reading log line from pubsub: %s' % ex)
pubsub = None
quit_now = True quit_now = True
exit_code = 2 exit_code = 2
@@ -403,21 +378,76 @@ def blacklistUpdate():
permBan(net=net, unban=True) permBan(net=net, unban=True)
time.sleep(60.0 - ((time.time() - start_time) % 60.0)) time.sleep(60.0 - ((time.time() - start_time) % 60.0))
def quit(signum, frame): def sigterm_quit(signum, frame):
global quit_now global clear_before_quit
quit_now = True clear_before_quit = True
sys.exit(exit_code)
def berfore_quit():
if clear_before_quit:
clear()
if pubsub is not None:
pubsub.unsubscribe()
if __name__ == '__main__': if __name__ == '__main__':
refreshF2boptions() atexit.register(berfore_quit)
signal.signal(signal.SIGTERM, sigterm_quit)
# init Logger
logger = Logger(None)
# init backend
backend = sys.argv[1]
if backend == "nftables":
logger.logInfo('Using NFTables backend')
tables = NFTables(chain_name, logger)
else:
logger.logInfo('Using IPTables backend')
tables = IPTables(chain_name, logger)
# In case a previous session was killed without cleanup # In case a previous session was killed without cleanup
clear() clear()
# Reinit MAILCOW chain # Reinit MAILCOW chain
# Is called before threads start, no locking # Is called before threads start, no locking
logger.logInfo("Initializing mailcow netfilter chain") logger.logInfo("Initializing mailcow netfilter chain")
tables.initChainIPv4() tables.initChainIPv4()
tables.initChainIPv6() tables.initChainIPv6()
if os.getenv("DISABLE_NETFILTER_ISOLATION_RULE").lower() in ("y", "yes"):
logger.logInfo(f"Skipping {chain_name} isolation")
else:
logger.logInfo(f"Setting {chain_name} isolation")
tables.create_mailcow_isolation_rule("br-mailcow", [3306, 6379, 8983, 12345], os.getenv("MAILCOW_REPLICA_IP"))
# connect to redis
while True:
try:
redis_slaveof_ip = os.getenv('REDIS_SLAVEOF_IP', '')
redis_slaveof_port = os.getenv('REDIS_SLAVEOF_PORT', '')
if "".__eq__(redis_slaveof_ip):
r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0)
else:
r = redis.StrictRedis(host=redis_slaveof_ip, decode_responses=True, port=redis_slaveof_port, db=0)
r.ping()
pubsub = r.pubsub()
except Exception as ex:
print('%s - trying again in 3 seconds' % (ex))
time.sleep(3)
else:
break
Logger.r = r
# rename fail2ban to netfilter
if r.exists('F2B_LOG'):
r.rename('F2B_LOG', 'NETFILTER_LOG')
# clear bans in redis
r.delete('F2B_ACTIVE_BANS')
r.delete('F2B_PERM_BANS')
refreshF2boptions()
watch_thread = Thread(target=watch) watch_thread = Thread(target=watch)
watch_thread.daemon = True watch_thread.daemon = True
watch_thread.start() watch_thread.start()
@@ -460,9 +490,6 @@ if __name__ == '__main__':
whitelistupdate_thread.daemon = True whitelistupdate_thread.daemon = True
whitelistupdate_thread.start() whitelistupdate_thread.start()
signal.signal(signal.SIGTERM, quit)
atexit.register(clear)
while not quit_now: while not quit_now:
time.sleep(0.5) time.sleep(0.5)
@@ -1,5 +1,6 @@
import iptc import iptc
import time import time
import os
class IPTables: class IPTables:
def __init__(self, chain_name, logger): def __init__(self, chain_name, logger):
@@ -211,3 +212,41 @@ class IPTables:
target = rule.create_target("SNAT") target = rule.create_target("SNAT")
target.to_source = snat_target target.to_source = snat_target
return rule return rule
def create_mailcow_isolation_rule(self, _interface:str, _dports:list, _allow:str = ""):
try:
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), self.chain_name)
# insert mailcow isolation rule
rule = iptc.Rule()
rule.in_interface = f'! {_interface}'
rule.out_interface = _interface
rule.protocol = 'tcp'
rule.create_target("DROP")
match = rule.create_match("multiport")
match.dports = ','.join(map(str, _dports))
if rule in chain.rules:
chain.delete_rule(rule)
chain.insert_rule(rule, position=0)
# insert mailcow isolation exception rule
if _allow != "":
rule = iptc.Rule()
rule.src = _allow
rule.in_interface = f'! {_interface}'
rule.out_interface = _interface
rule.protocol = 'tcp'
rule.create_target("ACCEPT")
match = rule.create_match("multiport")
match.dports = ','.join(map(str, _dports))
if rule in chain.rules:
chain.delete_rule(rule)
chain.insert_rule(rule, position=0)
return True
except Exception as e:
self.logger.logCrit(f"Error adding {self.chain_name} isolation: {e}")
return False
+2 -1
View File
@@ -10,7 +10,8 @@ class Logger:
tolog['time'] = int(round(time.time())) tolog['time'] = int(round(time.time()))
tolog['priority'] = priority tolog['priority'] = priority
tolog['message'] = message tolog['message'] = message
self.r.lpush('NETFILTER_LOG', json.dumps(tolog, ensure_ascii=False)) if self.r:
self.r.lpush('NETFILTER_LOG', json.dumps(tolog, ensure_ascii=False))
print(message) print(message)
def logWarn(self, message): def logWarn(self, message):
+163 -2
View File
@@ -1,5 +1,6 @@
import nftables import nftables
import ipaddress import ipaddress
import os
class NFTables: class NFTables:
def __init__(self, chain_name, logger): def __init__(self, chain_name, logger):
@@ -266,6 +267,17 @@ class NFTables:
return self.nft_exec_dict(delete_command) return self.nft_exec_dict(delete_command)
def delete_filter_rule(self, _family:str, _chain: str, _handle:str):
delete_command = self.get_base_dict()
_rule_opts = {'family': _family,
'table': 'filter',
'chain': _chain,
'handle': _handle }
_delete = {'delete': {'rule': _rule_opts} }
delete_command["nftables"].append(_delete)
return self.nft_exec_dict(delete_command)
def snat_rule(self, _family: str, snat_target: str, source_address: str): def snat_rule(self, _family: str, snat_target: str, source_address: str):
chain_name = self.nft_chain_names[_family]['nat']['postrouting'] chain_name = self.nft_chain_names[_family]['nat']['postrouting']
@@ -381,7 +393,7 @@ class NFTables:
break break
return chain_handle return chain_handle
def get_rules_handle(self, _family: str, _table: str, chain_name: str): def get_rules_handle(self, _family: str, _table: str, chain_name: str, _comment_filter = "mailcow"):
rule_handle = [] rule_handle = []
# Command: 'nft list chain {family} {table} {chain_name}' # Command: 'nft list chain {family} {table} {chain_name}'
_chain_opts = {'family': _family, 'table': _table, 'name': chain_name} _chain_opts = {'family': _family, 'table': _table, 'name': chain_name}
@@ -397,7 +409,7 @@ class NFTables:
rule = _object["rule"] rule = _object["rule"]
if rule["family"] == _family and rule["table"] == _table and rule["chain"] == chain_name: if rule["family"] == _family and rule["table"] == _table and rule["chain"] == chain_name:
if rule.get("comment") and rule["comment"] == "mailcow": if rule.get("comment") and rule["comment"] == _comment_filter:
rule_handle.append(rule["handle"]) rule_handle.append(rule["handle"])
return rule_handle return rule_handle
@@ -493,3 +505,152 @@ class NFTables:
position+=1 position+=1
return position if rule_found else False return position if rule_found else False
def create_mailcow_isolation_rule(self, _interface:str, _dports:list, _allow:str = ""):
family = "ip"
table = "filter"
comment_filter_drop = "mailcow isolation"
comment_filter_allow = "mailcow isolation allow"
json_command = self.get_base_dict()
# Delete old mailcow isolation rules
handles = self.get_rules_handle(family, table, self.chain_name, comment_filter_drop)
for handle in handles:
self.delete_filter_rule(family, self.chain_name, handle)
handles = self.get_rules_handle(family, table, self.chain_name, comment_filter_allow)
for handle in handles:
self.delete_filter_rule(family, self.chain_name, handle)
# insert mailcow isolation rule
_match_dict_drop = [
{
"match": {
"op": "!=",
"left": {
"meta": {
"key": "iifname"
}
},
"right": _interface
}
},
{
"match": {
"op": "==",
"left": {
"meta": {
"key": "oifname"
}
},
"right": _interface
}
},
{
"match": {
"op": "==",
"left": {
"payload": {
"protocol": "tcp",
"field": "dport"
}
},
"right": {
"set": _dports
}
}
},
{
"counter": {
"packets": 0,
"bytes": 0
}
},
{
"drop": None
}
]
rule_drop = { "insert": { "rule": {
"family": family,
"table": table,
"chain": self.chain_name,
"comment": comment_filter_drop,
"expr": _match_dict_drop
}}}
json_command["nftables"].append(rule_drop)
# insert mailcow isolation allow rule
if _allow != "":
_match_dict_allow = [
{
"match": {
"op": "==",
"left": {
"payload": {
"protocol": "ip",
"field": "saddr"
}
},
"right": _allow
}
},
{
"match": {
"op": "!=",
"left": {
"meta": {
"key": "iifname"
}
},
"right": _interface
}
},
{
"match": {
"op": "==",
"left": {
"meta": {
"key": "oifname"
}
},
"right": _interface
}
},
{
"match": {
"op": "==",
"left": {
"payload": {
"protocol": "tcp",
"field": "dport"
}
},
"right": {
"set": _dports
}
}
},
{
"counter": {
"packets": 0,
"bytes": 0
}
},
{
"accept": None
}
]
rule_allow = { "insert": { "rule": {
"family": family,
"table": table,
"chain": self.chain_name,
"comment": comment_filter_allow,
"expr": _match_dict_allow
}}}
json_command["nftables"].append(rule_allow)
success = self.nft_exec_dict(json_command)
if success == False:
self.logger.logCrit(f"Error adding {self.chain_name} isolation")
return False
return True
+1 -1
View File
@@ -1,4 +1,4 @@
FROM php:8.2-fpm-alpine3.19 FROM php:8.2-fpm-alpine3.18
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$ # renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
+4 -3
View File
@@ -1,7 +1,8 @@
FROM debian:bullseye-slim FROM debian:bookworm-slim
LABEL maintainer "The Infrastructure Company GmbH GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ARG DEBIAN_VERSION=bookworm
ARG SOGO_DEBIAN_REPOSITORY=http://www.axis.cz/linux/debian ARG SOGO_DEBIAN_REPOSITORY=http://www.axis.cz/linux/debian
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$ # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.17 ARG GOSU_VERSION=1.17
@@ -21,7 +22,7 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
syslog-ng-core \ syslog-ng-core \
syslog-ng-mod-redis \ syslog-ng-mod-redis \
dirmngr \ dirmngr \
netcat \ netcat-traditional \
psmisc \ psmisc \
wget \ wget \
patch \ patch \
@@ -32,7 +33,7 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
&& mkdir /usr/share/doc/sogo \ && mkdir /usr/share/doc/sogo \
&& touch /usr/share/doc/sogo/empty.sh \ && touch /usr/share/doc/sogo/empty.sh \
&& apt-key adv --keyserver keys.openpgp.org --recv-key 74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9 \ && apt-key adv --keyserver keys.openpgp.org --recv-key 74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9 \
&& echo "deb [trusted=yes] ${SOGO_DEBIAN_REPOSITORY} bullseye sogo-v5" > /etc/apt/sources.list.d/sogo.list \ && echo "deb [trusted=yes] ${SOGO_DEBIAN_REPOSITORY} ${DEBIAN_VERSION} sogo-v5" > /etc/apt/sources.list.d/sogo.list \
&& apt-get update && apt-get install -y --no-install-recommends \ && apt-get update && apt-get install -y --no-install-recommends \
sogo \ sogo \
sogo-activesync \ sogo-activesync \
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.19 FROM alpine:3.18
LABEL maintainer "The Infrastructure Company GmbH GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH GmbH <info@servercow.de>"
+10
View File
@@ -1,5 +1,10 @@
#!/bin/bash #!/bin/bash
# Skip Unbound (DNS Resolver) Healthchecks (NOT Recommended!)
if [[ "${SKIP_UNBOUND_HEALTHCHECK}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
SKIP_UNBOUND_HEALTHCHECK=y
fi
# Declare log function for logfile inside container # Declare log function for logfile inside container
function log_to_file() { function log_to_file() {
echo "$(date +"%Y-%m-%d %H:%M:%S"): $1" > /var/log/healthcheck.log echo "$(date +"%Y-%m-%d %H:%M:%S"): $1" > /var/log/healthcheck.log
@@ -66,6 +71,11 @@ function check_netcat() {
} }
if [[ ${SKIP_UNBOUND_HEALTHCHECK} == "y" ]]; then
log_to_file "Healthcheck: ALL CHECKS WERE SKIPPED! Unbound is healthy!"
exit 0
fi
# run checks, if check is not returning 0 (return value if check is ok), healthcheck will exit with 1 (marked in docker as unhealthy) # run checks, if check is not returning 0 (return value if check is ok), healthcheck will exit with 1 (marked in docker as unhealthy)
check_ping check_ping
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.19 FROM alpine:3.18
LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer "The Infrastructure Company GmbH <info@servercow.de>"
# Installation # Installation
+1 -1
View File
@@ -170,7 +170,7 @@ function notify_error() {
fi fi
# Replace subject and body placeholders # Replace subject and body placeholders
WEBHOOK_BODY=$(echo ${WATCHDOG_NOTIFY_WEBHOOK_BODY} | sed "s|\$SUBJECT\|\${SUBJECT}|$SUBJECT|g" | sed "s|\$BODY\|\${BODY}|$BODY|") WEBHOOK_BODY=$(echo ${WATCHDOG_NOTIFY_WEBHOOK_BODY} | sed "s/\$SUBJECT\|\${SUBJECT}/$SUBJECT/g" | sed "s/\$BODY\|\${BODY}/$BODY/g")
# POST to webhook # POST to webhook
curl -X POST -H "Content-Type: application/json" ${CURL_VERBOSE} -d "${WEBHOOK_BODY}" ${WATCHDOG_NOTIFY_WEBHOOK} curl -X POST -H "Content-Type: application/json" ${CURL_VERBOSE} -d "${WEBHOOK_BODY}" ${WATCHDOG_NOTIFY_WEBHOOK}
+3
View File
@@ -247,6 +247,9 @@ plugin {
mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
mail_log_fields = uid box msgid size mail_log_fields = uid box msgid size
mail_log_cached_only = yes mail_log_cached_only = yes
# Try set mail_replica
!include_try /etc/dovecot/mail_replica.conf
} }
service quota-warning { service quota-warning {
executable = script /usr/local/bin/quota_notify.py executable = script /usr/local/bin/quota_notify.py
+59 -22
View File
@@ -1,9 +1,10 @@
# Whitelist generated by Postwhite v3.4 on Mon Jan 1 00:15:22 UTC 2024 # Whitelist generated by Postwhite v3.4 on Thu Feb 1 00:13:50 UTC 2024
# https://github.com/stevejenkins/postwhite/ # https://github.com/stevejenkins/postwhite/
# 2052 total rules # 2089 total rules
2a00:1450:4000::/36 permit 2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit 2a01:111:f400::/48 permit
2a01:111:f403:8000::/50 permit 2a01:111:f403:8000::/50 permit
2a01:111:f403:8000::/51 permit
2a01:111:f403::/49 permit 2a01:111:f403::/49 permit
2a01:111:f403:c000::/51 permit 2a01:111:f403:c000::/51 permit
2a01:111:f403:f000::/52 permit 2a01:111:f403:f000::/52 permit
@@ -116,7 +117,6 @@
40.92.0.0/16 permit 40.92.0.0/16 permit
40.107.0.0/16 permit 40.107.0.0/16 permit
40.112.65.63 permit 40.112.65.63 permit
40.117.80.0/24 permit
43.228.184.0/22 permit 43.228.184.0/22 permit
44.206.138.57 permit 44.206.138.57 permit
44.209.42.157 permit 44.209.42.157 permit
@@ -206,7 +206,6 @@
52.95.49.88/29 permit 52.95.49.88/29 permit
52.96.91.34 permit 52.96.91.34 permit
52.96.111.82 permit 52.96.111.82 permit
52.96.172.98 permit
52.96.214.50 permit 52.96.214.50 permit
52.96.222.194 permit 52.96.222.194 permit
52.96.222.226 permit 52.96.222.226 permit
@@ -405,7 +404,6 @@
66.196.81.228/30 permit 66.196.81.228/30 permit
66.196.81.232/31 permit 66.196.81.232/31 permit
66.196.81.234 permit 66.196.81.234 permit
66.211.168.230/31 permit
66.211.170.88/29 permit 66.211.170.88/29 permit
66.211.184.0/23 permit 66.211.184.0/23 permit
66.218.74.64/30 permit 66.218.74.64/30 permit
@@ -594,9 +592,10 @@
74.112.67.243 permit 74.112.67.243 permit
74.125.0.0/16 permit 74.125.0.0/16 permit
74.202.227.40 permit 74.202.227.40 permit
74.208.4.192/26 permit 74.208.4.200 permit
74.208.5.64/26 permit 74.208.4.201 permit
74.208.122.0/26 permit 74.208.4.220 permit
74.208.4.221 permit
74.209.250.0/24 permit 74.209.250.0/24 permit
75.2.70.75 permit 75.2.70.75 permit
76.223.128.0/19 permit 76.223.128.0/19 permit
@@ -624,12 +623,20 @@
77.238.189.148/30 permit 77.238.189.148/30 permit
81.7.169.128/25 permit 81.7.169.128/25 permit
81.223.46.0/27 permit 81.223.46.0/27 permit
82.165.159.0/24 permit 82.165.159.2 permit
82.165.159.0/26 permit 82.165.159.3 permit
82.165.229.31 permit 82.165.159.4 permit
82.165.229.130 permit 82.165.159.12 permit
82.165.230.21 permit 82.165.159.13 permit
82.165.230.22 permit 82.165.159.14 permit
82.165.159.34 permit
82.165.159.35 permit
82.165.159.40 permit
82.165.159.41 permit
82.165.159.42 permit
82.165.159.45 permit
82.165.159.130 permit
82.165.159.131 permit
84.116.6.0/23 permit 84.116.6.0/23 permit
84.116.36.0/24 permit 84.116.36.0/24 permit
84.116.50.0/23 permit 84.116.50.0/23 permit
@@ -1430,6 +1437,7 @@
135.84.216.0/22 permit 135.84.216.0/22 permit
136.143.160.0/24 permit 136.143.160.0/24 permit
136.143.161.0/24 permit 136.143.161.0/24 permit
136.143.178.49 permit
136.143.182.0/23 permit 136.143.182.0/23 permit
136.143.184.0/24 permit 136.143.184.0/24 permit
136.143.188.0/24 permit 136.143.188.0/24 permit
@@ -1952,12 +1960,41 @@
212.82.111.228/31 permit 212.82.111.228/31 permit
212.82.111.230 permit 212.82.111.230 permit
212.123.28.40 permit 212.123.28.40 permit
212.227.15.0/24 permit 212.227.15.3 permit
212.227.15.0/25 permit 212.227.15.4 permit
212.227.17.0/27 permit 212.227.15.5 permit
212.227.126.128/25 permit 212.227.15.6 permit
212.227.15.14 permit
212.227.15.15 permit
212.227.15.18 permit
212.227.15.19 permit
212.227.15.25 permit
212.227.15.26 permit
212.227.15.29 permit
212.227.15.44 permit
212.227.15.45 permit
212.227.15.46 permit
212.227.15.47 permit
212.227.15.50 permit
212.227.15.52 permit
212.227.15.53 permit
212.227.15.54 permit
212.227.15.55 permit
212.227.17.11 permit
212.227.17.12 permit
212.227.17.18 permit
212.227.17.19 permit
212.227.17.20 permit
212.227.17.21 permit
212.227.17.22 permit
212.227.17.26 permit
212.227.17.28 permit
212.227.17.29 permit
212.227.126.224 permit
212.227.126.225 permit
212.227.126.226 permit
212.227.126.227 permit
213.46.255.0/24 permit 213.46.255.0/24 permit
213.165.64.0/23 permit
213.199.128.139 permit 213.199.128.139 permit
213.199.128.145 permit 213.199.128.145 permit
213.199.138.181 permit 213.199.138.181 permit
@@ -2021,10 +2058,10 @@
216.203.30.55 permit 216.203.30.55 permit
216.203.33.178/31 permit 216.203.33.178/31 permit
216.205.24.0/24 permit 216.205.24.0/24 permit
216.221.160.0/19 permit
216.239.32.0/19 permit 216.239.32.0/19 permit
217.72.192.64/26 permit 217.72.192.77 permit
217.72.192.248/29 permit 217.72.192.78 permit
217.72.207.0/27 permit
217.77.141.52 permit 217.77.141.52 permit
217.77.141.59 permit 217.77.141.59 permit
217.175.194.0/24 permit 217.175.194.0/24 permit
+18 -13
View File
@@ -2,9 +2,10 @@ version: '2.1'
services: services:
unbound-mailcow: unbound-mailcow:
image: mailcow/unbound:1.19.1 image: mailcow/unbound:1.20
environment: environment:
- TZ=${TZ} - TZ=${TZ}
- SKIP_UNBOUND_HEALTHCHECK=${SKIP_UNBOUND_HEALTHCHECK:-n}
volumes: volumes:
- ./data/hooks/unbound:/hooks:Z - ./data/hooks/unbound:/hooks:Z
- ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro,Z - ./data/conf/unbound/unbound.conf:/etc/unbound/unbound.conf:ro,Z
@@ -20,6 +21,7 @@ services:
image: mariadb:10.5 image: mariadb:10.5
depends_on: depends_on:
- unbound-mailcow - unbound-mailcow
- netfilter-mailcow
stop_grace_period: 45s stop_grace_period: 45s
volumes: volumes:
- mysql-vol-1:/var/lib/mysql/ - mysql-vol-1:/var/lib/mysql/
@@ -45,6 +47,8 @@ services:
volumes: volumes:
- redis-vol-1:/data/ - redis-vol-1:/data/
restart: always restart: always
depends_on:
- netfilter-mailcow
ports: ports:
- "${REDIS_PORT:-127.0.0.1:7654}:6379" - "${REDIS_PORT:-127.0.0.1:7654}:6379"
environment: environment:
@@ -107,7 +111,7 @@ services:
- rspamd - rspamd
php-fpm-mailcow: php-fpm-mailcow:
image: mailcow/phpfpm:1.86 image: mailcow/phpfpm:1.87
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:
- redis-mailcow - redis-mailcow
@@ -171,7 +175,7 @@ services:
- phpfpm - phpfpm
sogo-mailcow: sogo-mailcow:
image: mailcow/sogo:1.121 image: mailcow/sogo:1.122
environment: environment:
- DBNAME=${DBNAME} - DBNAME=${DBNAME}
- DBUSER=${DBUSER} - DBUSER=${DBUSER}
@@ -218,9 +222,10 @@ services:
- sogo - sogo
dovecot-mailcow: dovecot-mailcow:
image: mailcow/dovecot:1.27 image: mailcow/dovecot:1.28
depends_on: depends_on:
- mysql-mailcow - mysql-mailcow
- netfilter-mailcow
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
cap_add: cap_add:
@@ -241,6 +246,8 @@ services:
environment: environment:
- DOVECOT_MASTER_USER=${DOVECOT_MASTER_USER:-} - DOVECOT_MASTER_USER=${DOVECOT_MASTER_USER:-}
- DOVECOT_MASTER_PASS=${DOVECOT_MASTER_PASS:-} - DOVECOT_MASTER_PASS=${DOVECOT_MASTER_PASS:-}
- MAILCOW_REPLICA_IP=${MAILCOW_REPLICA_IP:-}
- DOVEADM_REPLICA_PORT=${DOVEADM_REPLICA_PORT:-}
- LOG_LINES=${LOG_LINES:-9999} - LOG_LINES=${LOG_LINES:-9999}
- DBNAME=${DBNAME} - DBNAME=${DBNAME}
- DBUSER=${DBUSER} - DBUSER=${DBUSER}
@@ -398,7 +405,7 @@ services:
condition: service_started condition: service_started
unbound-mailcow: unbound-mailcow:
condition: service_healthy condition: service_healthy
image: mailcow/acme:1.86 image: mailcow/acme:1.87
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
environment: environment:
@@ -434,14 +441,8 @@ services:
- acme - acme
netfilter-mailcow: netfilter-mailcow:
image: mailcow/netfilter:1.55 image: mailcow/netfilter:1.56
stop_grace_period: 30s stop_grace_period: 30s
depends_on:
- dovecot-mailcow
- postfix-mailcow
- sogo-mailcow
- php-fpm-mailcow
- redis-mailcow
restart: always restart: always
privileged: true privileged: true
environment: environment:
@@ -452,12 +453,14 @@ services:
- SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n} - SNAT6_TO_SOURCE=${SNAT6_TO_SOURCE:-n}
- REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-} - REDIS_SLAVEOF_IP=${REDIS_SLAVEOF_IP:-}
- REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-} - REDIS_SLAVEOF_PORT=${REDIS_SLAVEOF_PORT:-}
- MAILCOW_REPLICA_IP=${MAILCOW_REPLICA_IP:-}
- DISABLE_NETFILTER_ISOLATION_RULE=${DISABLE_NETFILTER_ISOLATION_RULE:-n}
network_mode: "host" network_mode: "host"
volumes: volumes:
- /lib/modules:/lib/modules:ro - /lib/modules:/lib/modules:ro
watchdog-mailcow: watchdog-mailcow:
image: mailcow/watchdog:2.01 image: mailcow/watchdog:2.02
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
tmpfs: tmpfs:
@@ -552,6 +555,8 @@ services:
solr-mailcow: solr-mailcow:
image: mailcow/solr:1.8.2 image: mailcow/solr:1.8.2
restart: always restart: always
depends_on:
- netfilter-mailcow
volumes: volumes:
- solr-vol-1:/opt/solr/server/solr/dovecot-fts/data - solr-vol-1:/opt/solr/server/solr/dovecot-fts/data
ports: ports:
+7
View File
@@ -363,6 +363,10 @@ SKIP_IP_CHECK=n
SKIP_HTTP_VERIFICATION=n SKIP_HTTP_VERIFICATION=n
# Skip Unbound (DNS Resolver) Healthchecks (NOT Recommended!) - y/n
SKIP_UNBOUND_HEALTHCHECK=n
# Skip ClamAV (clamd-mailcow) anti-virus (Rspamd will auto-detect a missing ClamAV container) - y/n # Skip ClamAV (clamd-mailcow) anti-virus (Rspamd will auto-detect a missing ClamAV container) - y/n
SKIP_CLAMD=${SKIP_CLAMD} SKIP_CLAMD=${SKIP_CLAMD}
@@ -490,6 +494,9 @@ WEBAUTHN_ONLY_TRUSTED_VENDORS=n
# Otherwise it will work normally. # Otherwise it will work normally.
SPAMHAUS_DQS_KEY= SPAMHAUS_DQS_KEY=
# Prevent netfilter from setting an iptables/nftables rule to isolate the mailcow docker network - y/n
# CAUTION: Disabling this may expose container ports to other neighbors on the same subnet, even if the ports are bound to localhost
DISABLE_NETFILTER_ISOLATION_RULE=n
EOF EOF
mkdir -p data/assets/ssl mkdir -p data/assets/ssl
+17 -2
View File
@@ -116,11 +116,11 @@ migrate_docker_nat() {
echo "Working on IPv6 NAT, please wait..." echo "Working on IPv6 NAT, please wait..."
echo ${NAT_CONFIG} > /etc/docker/daemon.json echo ${NAT_CONFIG} > /etc/docker/daemon.json
ip6tables -F -t nat ip6tables -F -t nat
[[ -e /etc/alpine-release ]] && rc-service docker restart || systemctl restart docker.service [[ -e /etc/rc.conf ]] && rc-service docker restart || systemctl restart docker.service
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
echo -e "\e[31mError:\e[0m Failed to activate IPv6 NAT! Reverting and exiting." echo -e "\e[31mError:\e[0m Failed to activate IPv6 NAT! Reverting and exiting."
rm /etc/docker/daemon.json rm /etc/docker/daemon.json
if [[ -e /etc/alpine-release ]]; then if [[ -e /etc/rc.conf ]]; then
rc-service docker restart rc-service docker restart
else else
systemctl reset-failed docker.service systemctl reset-failed docker.service
@@ -480,6 +480,8 @@ CONFIG_ARRAY=(
"WATCHDOG_VERBOSE" "WATCHDOG_VERBOSE"
"WEBAUTHN_ONLY_TRUSTED_VENDORS" "WEBAUTHN_ONLY_TRUSTED_VENDORS"
"SPAMHAUS_DQS_KEY" "SPAMHAUS_DQS_KEY"
"SKIP_UNBOUND_HEALTHCHECK"
"DISABLE_NETFILTER_ISOLATION_RULE"
) )
detect_bad_asn detect_bad_asn
@@ -747,6 +749,19 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# Enable watchdog verbose logging' >> mailcow.conf echo '# Enable watchdog verbose logging' >> mailcow.conf
echo 'WATCHDOG_VERBOSE=n' >> mailcow.conf echo 'WATCHDOG_VERBOSE=n' >> mailcow.conf
fi fi
elif [[ ${option} == "SKIP_UNBOUND_HEALTHCHECK" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Skip Unbound (DNS Resolver) Healthchecks (NOT Recommended!) - y/n' >> mailcow.conf
echo 'SKIP_UNBOUND_HEALTHCHECK=n' >> mailcow.conf
fi
elif [[ ${option} == "DISABLE_NETFILTER_ISOLATION_RULE" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf"
echo '# Prevent netfilter from setting an iptables/nftables rule to isolate the mailcow docker network - y/n' >> mailcow.conf
echo '# CAUTION: Disabling this may expose container ports to other neighbors on the same subnet, even if the ports are bound to localhost' >> mailcow.conf
echo 'DISABLE_NETFILTER_ISOLATION_RULE=n' >> mailcow.conf
fi
elif ! grep -q ${option} mailcow.conf; then elif ! grep -q ${option} mailcow.conf; then
echo "Adding new option \"${option}\" to mailcow.conf" echo "Adding new option \"${option}\" to mailcow.conf"
echo "${option}=n" >> mailcow.conf echo "${option}=n" >> mailcow.conf