commit
1e09df20b6
@ -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
.gitignore
vendored
1
.gitignore
vendored
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
@ -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):
|
||||||
|
@ -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,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 \
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -21,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/
|
||||||
@ -46,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:
|
||||||
@ -172,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}
|
||||||
@ -219,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:
|
||||||
@ -242,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}
|
||||||
@ -435,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:
|
||||||
@ -453,6 +453,8 @@ 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
|
||||||
@ -553,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:
|
||||||
|
@ -494,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
|
||||||
|
@ -481,6 +481,7 @@ CONFIG_ARRAY=(
|
|||||||
"WEBAUTHN_ONLY_TRUSTED_VENDORS"
|
"WEBAUTHN_ONLY_TRUSTED_VENDORS"
|
||||||
"SPAMHAUS_DQS_KEY"
|
"SPAMHAUS_DQS_KEY"
|
||||||
"SKIP_UNBOUND_HEALTHCHECK"
|
"SKIP_UNBOUND_HEALTHCHECK"
|
||||||
|
"DISABLE_NETFILTER_ISOLATION_RULE"
|
||||||
)
|
)
|
||||||
|
|
||||||
detect_bad_asn
|
detect_bad_asn
|
||||||
@ -754,6 +755,13 @@ for option in ${CONFIG_ARRAY[@]}; do
|
|||||||
echo '# Skip Unbound (DNS Resolver) Healthchecks (NOT Recommended!) - y/n' >> mailcow.conf
|
echo '# Skip Unbound (DNS Resolver) Healthchecks (NOT Recommended!) - y/n' >> mailcow.conf
|
||||||
echo 'SKIP_UNBOUND_HEALTHCHECK=n' >> mailcow.conf
|
echo 'SKIP_UNBOUND_HEALTHCHECK=n' >> mailcow.conf
|
||||||
fi
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user