|
@@ -1,4 +1,4 @@
|
|
-#!/usr/bin/env python2
|
|
|
|
|
|
+#!/usr/bin/env python3
|
|
|
|
|
|
import re
|
|
import re
|
|
import os
|
|
import os
|
|
@@ -21,7 +21,7 @@ while True:
|
|
r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0)
|
|
r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0)
|
|
r.ping()
|
|
r.ping()
|
|
except Exception as ex:
|
|
except Exception as ex:
|
|
- print '%s - trying again in 3 seconds' % (ex)
|
|
|
|
|
|
+ print('%s - trying again in 3 seconds' % (ex))
|
|
time.sleep(3)
|
|
time.sleep(3)
|
|
else:
|
|
else:
|
|
break
|
|
break
|
|
@@ -66,8 +66,8 @@ def refreshF2boptions():
|
|
try:
|
|
try:
|
|
f2boptions = {}
|
|
f2boptions = {}
|
|
f2boptions = json.loads(r.get('F2B_OPTIONS'))
|
|
f2boptions = json.loads(r.get('F2B_OPTIONS'))
|
|
- except ValueError, e:
|
|
|
|
- print 'Error loading F2B options: F2B_OPTIONS is not json'
|
|
|
|
|
|
+ except ValueError as e:
|
|
|
|
+ print('Error loading F2B options: F2B_OPTIONS is not json')
|
|
quit_now = True
|
|
quit_now = True
|
|
|
|
|
|
if r.exists('F2B_LOG'):
|
|
if r.exists('F2B_LOG'):
|
|
@@ -96,14 +96,14 @@ def mailcowChainOrder():
|
|
log['priority'] = 'crit'
|
|
log['priority'] = 'crit'
|
|
log['message'] = 'Error in ' + chain.name + ' chain order, restarting container'
|
|
log['message'] = 'Error in ' + chain.name + ' chain order, restarting container'
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print log['message']
|
|
|
|
|
|
+ print(log['message'])
|
|
quit_now = True
|
|
quit_now = True
|
|
if not target_found:
|
|
if not target_found:
|
|
log['time'] = int(round(time.time()))
|
|
log['time'] = int(round(time.time()))
|
|
log['priority'] = 'crit'
|
|
log['priority'] = 'crit'
|
|
log['message'] = 'Error in ' + chain.name + ' chain: MAILCOW target not found, restarting container'
|
|
log['message'] = 'Error in ' + chain.name + ' chain: MAILCOW target not found, restarting container'
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print log['message']
|
|
|
|
|
|
+ print(log['message'])
|
|
quit_now = True
|
|
quit_now = True
|
|
|
|
|
|
def ban(address):
|
|
def ban(address):
|
|
@@ -133,7 +133,7 @@ def ban(address):
|
|
log['priority'] = 'info'
|
|
log['priority'] = 'info'
|
|
log['message'] = 'Address %s is whitelisted by rule %s' % (self_network, wl_net)
|
|
log['message'] = 'Address %s is whitelisted by rule %s' % (self_network, wl_net)
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print 'Address %s is whitelisted by rule %s' % (self_network, wl_net)
|
|
|
|
|
|
+ print('Address %s is whitelisted by rule %s' % (self_network, wl_net))
|
|
return
|
|
return
|
|
|
|
|
|
net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)).decode('ascii'), strict=False)
|
|
net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)).decode('ascii'), strict=False)
|
|
@@ -155,7 +155,7 @@ def ban(address):
|
|
log['priority'] = 'crit'
|
|
log['priority'] = 'crit'
|
|
log['message'] = 'Banning %s' % net
|
|
log['message'] = 'Banning %s' % net
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print 'Banning %s for %d minutes' % (net, BAN_TIME / 60)
|
|
|
|
|
|
+ print('Banning %s for %d minutes' % (net, BAN_TIME / 60))
|
|
if type(ip) is ipaddress.IPv4Address:
|
|
if type(ip) is ipaddress.IPv4Address:
|
|
with lock:
|
|
with lock:
|
|
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
|
|
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
|
|
@@ -180,7 +180,7 @@ def ban(address):
|
|
log['priority'] = 'warn'
|
|
log['priority'] = 'warn'
|
|
log['message'] = '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net)
|
|
log['message'] = '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net)
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print '%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net)
|
|
|
|
|
|
+ print('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
|
|
|
|
|
|
def unban(net):
|
|
def unban(net):
|
|
global lock
|
|
global lock
|
|
@@ -190,12 +190,12 @@ def unban(net):
|
|
if not net in bans:
|
|
if not net in bans:
|
|
log['message'] = '%s is not banned, skipping unban and deleting from queue (if any)' % net
|
|
log['message'] = '%s is not banned, skipping unban and deleting from queue (if any)' % net
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print '%s is not banned, skipping unban and deleting from queue (if any)' % net
|
|
|
|
|
|
+ print('%s is not banned, skipping unban and deleting from queue (if any)' % net)
|
|
r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
|
|
r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
|
|
return
|
|
return
|
|
log['message'] = 'Unbanning %s' % net
|
|
log['message'] = 'Unbanning %s' % net
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print 'Unbanning %s' % net
|
|
|
|
|
|
+ print('Unbanning %s' % net)
|
|
if type(ipaddress.ip_network(net.decode('ascii'))) is ipaddress.IPv4Network:
|
|
if type(ipaddress.ip_network(net.decode('ascii'))) is ipaddress.IPv4Network:
|
|
with lock:
|
|
with lock:
|
|
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
|
|
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
|
|
@@ -229,7 +229,7 @@ def clear():
|
|
log['priority'] = 'info'
|
|
log['priority'] = 'info'
|
|
log['message'] = 'Clearing all bans'
|
|
log['message'] = 'Clearing all bans'
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print 'Clearing all bans'
|
|
|
|
|
|
+ print('Clearing all bans')
|
|
for net in bans.copy():
|
|
for net in bans.copy():
|
|
unban(net)
|
|
unban(net)
|
|
with lock:
|
|
with lock:
|
|
@@ -263,11 +263,11 @@ def watch():
|
|
log['message'] = 'Watching Redis channel F2B_CHANNEL'
|
|
log['message'] = 'Watching Redis channel F2B_CHANNEL'
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
pubsub.subscribe('F2B_CHANNEL')
|
|
pubsub.subscribe('F2B_CHANNEL')
|
|
- print 'Subscribing to Redis channel F2B_CHANNEL'
|
|
|
|
|
|
+ print('Subscribing to Redis channel F2B_CHANNEL')
|
|
|
|
|
|
while not quit_now:
|
|
while not quit_now:
|
|
for item in pubsub.listen():
|
|
for item in pubsub.listen():
|
|
- for rule_id, rule_regex in RULES.iteritems():
|
|
|
|
|
|
+ for rule_id, rule_regex in RULES.items():
|
|
if item['data'] and item['type'] == 'message':
|
|
if item['data'] and item['type'] == 'message':
|
|
result = re.search(rule_regex, item['data'])
|
|
result = re.search(rule_regex, item['data'])
|
|
if result:
|
|
if result:
|
|
@@ -275,7 +275,7 @@ def watch():
|
|
ip = ipaddress.ip_address(addr.decode('ascii'))
|
|
ip = ipaddress.ip_address(addr.decode('ascii'))
|
|
if ip.is_private or ip.is_loopback:
|
|
if ip.is_private or ip.is_loopback:
|
|
continue
|
|
continue
|
|
- print '%s matched rule id %d' % (addr, rule_id)
|
|
|
|
|
|
+ print('%s matched rule id %d' % (addr, rule_id))
|
|
log['time'] = int(round(time.time()))
|
|
log['time'] = int(round(time.time()))
|
|
log['priority'] = 'warn'
|
|
log['priority'] = 'warn'
|
|
log['message'] = '%s matched rule id %d' % (addr, rule_id)
|
|
log['message'] = '%s matched rule id %d' % (addr, rule_id)
|
|
@@ -307,7 +307,7 @@ def snat4(snat_target):
|
|
log['priority'] = 'info'
|
|
log['priority'] = 'info'
|
|
log['message'] = 'Added POSTROUTING rule for source network ' + get_snat4_rule().src + ' to SNAT target ' + snat_target
|
|
log['message'] = 'Added POSTROUTING rule for source network ' + get_snat4_rule().src + ' to SNAT target ' + snat_target
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print log['message']
|
|
|
|
|
|
+ print(log['message'])
|
|
chain.insert_rule(get_snat4_rule())
|
|
chain.insert_rule(get_snat4_rule())
|
|
table.commit()
|
|
table.commit()
|
|
else:
|
|
else:
|
|
@@ -318,7 +318,7 @@ def snat4(snat_target):
|
|
table.commit()
|
|
table.commit()
|
|
table.autocommit = True
|
|
table.autocommit = True
|
|
except:
|
|
except:
|
|
- print 'Error running SNAT4, retrying...'
|
|
|
|
|
|
+ print('Error running SNAT4, retrying...')
|
|
|
|
|
|
def snat6(snat_target):
|
|
def snat6(snat_target):
|
|
global lock
|
|
global lock
|
|
@@ -345,7 +345,7 @@ def snat6(snat_target):
|
|
log['priority'] = 'info'
|
|
log['priority'] = 'info'
|
|
log['message'] = 'Added POSTROUTING rule for source network ' + get_snat6_rule().src + ' to SNAT target ' + snat_target
|
|
log['message'] = 'Added POSTROUTING rule for source network ' + get_snat6_rule().src + ' to SNAT target ' + snat_target
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print log['message']
|
|
|
|
|
|
+ print(log['message'])
|
|
chain.insert_rule(get_snat6_rule())
|
|
chain.insert_rule(get_snat6_rule())
|
|
table.commit()
|
|
table.commit()
|
|
else:
|
|
else:
|
|
@@ -356,7 +356,7 @@ def snat6(snat_target):
|
|
table.commit()
|
|
table.commit()
|
|
table.autocommit = True
|
|
table.autocommit = True
|
|
except:
|
|
except:
|
|
- print 'Error running SNAT6, retrying...'
|
|
|
|
|
|
+ print('Error running SNAT6, retrying...')
|
|
|
|
|
|
def autopurge():
|
|
def autopurge():
|
|
while not quit_now:
|
|
while not quit_now:
|
|
@@ -401,7 +401,7 @@ def genNetworkList(list):
|
|
log['priority'] = 'info'
|
|
log['priority'] = 'info'
|
|
log['message'] = 'Hostname %s timedout on resolve' % (hostname)
|
|
log['message'] = 'Hostname %s timedout on resolve' % (hostname)
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print 'Hostname %s timedout on resolve' % (hostname)
|
|
|
|
|
|
+ print('Hostname %s timedout on resolve' % (hostname))
|
|
break
|
|
break
|
|
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
|
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
|
continue
|
|
continue
|
|
@@ -410,7 +410,7 @@ def genNetworkList(list):
|
|
log['priority'] = 'info'
|
|
log['priority'] = 'info'
|
|
log['message'] = '%s' % (dnsexception)
|
|
log['message'] = '%s' % (dnsexception)
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print '%s' % (dnsexception)
|
|
|
|
|
|
+ print('%s' % (dnsexception))
|
|
continue
|
|
continue
|
|
|
|
|
|
for rdata in answer:
|
|
for rdata in answer:
|
|
@@ -436,13 +436,13 @@ def whitelistUpdate():
|
|
log['priority'] = 'info'
|
|
log['priority'] = 'info'
|
|
log['message'] = 'New entrys for whitelist %s' % (WHITELIST)
|
|
log['message'] = 'New entrys for whitelist %s' % (WHITELIST)
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print 'New entrys for whitelist %s' % (WHITELIST)
|
|
|
|
|
|
+ print('New entrys for whitelist %s' % (WHITELIST))
|
|
|
|
|
|
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
|
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
|
|
|
|
|
def initChain():
|
|
def initChain():
|
|
# Is called before threads start, no locking
|
|
# Is called before threads start, no locking
|
|
- print "Initializing mailcow netfilter chain"
|
|
|
|
|
|
+ print("Initializing mailcow netfilter chain")
|
|
# IPv4
|
|
# IPv4
|
|
if not iptc.Chain(iptc.Table(iptc.Table.FILTER), "MAILCOW") in iptc.Table(iptc.Table.FILTER).chains:
|
|
if not iptc.Chain(iptc.Table(iptc.Table.FILTER), "MAILCOW") in iptc.Table(iptc.Table.FILTER).chains:
|
|
iptc.Table(iptc.Table.FILTER).create_chain("MAILCOW")
|
|
iptc.Table(iptc.Table.FILTER).create_chain("MAILCOW")
|
|
@@ -482,7 +482,7 @@ def initChain():
|
|
log['priority'] = 'crit'
|
|
log['priority'] = 'crit'
|
|
log['message'] = 'Blacklisting host/network %s' % bl_key
|
|
log['message'] = 'Blacklisting host/network %s' % bl_key
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print log['message']
|
|
|
|
|
|
+ print(log['message'])
|
|
chain.insert_rule(rule)
|
|
chain.insert_rule(rule)
|
|
r.hset('F2B_PERM_BANS', '%s' % bl_key, int(round(time.time())))
|
|
r.hset('F2B_PERM_BANS', '%s' % bl_key, int(round(time.time())))
|
|
else:
|
|
else:
|
|
@@ -496,7 +496,7 @@ def initChain():
|
|
log['priority'] = 'crit'
|
|
log['priority'] = 'crit'
|
|
log['message'] = 'Blacklisting host/network %s' % bl_key
|
|
log['message'] = 'Blacklisting host/network %s' % bl_key
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
|
|
- print log['message']
|
|
|
|
|
|
+ print(log['message'])
|
|
chain.insert_rule(rule)
|
|
chain.insert_rule(rule)
|
|
r.hset('F2B_PERM_BANS', '%s' % bl_key, int(round(time.time())))
|
|
r.hset('F2B_PERM_BANS', '%s' % bl_key, int(round(time.time())))
|
|
|
|
|
|
@@ -520,7 +520,7 @@ if __name__ == '__main__':
|
|
snat4_thread.daemon = True
|
|
snat4_thread.daemon = True
|
|
snat4_thread.start()
|
|
snat4_thread.start()
|
|
except ValueError:
|
|
except ValueError:
|
|
- print os.getenv('SNAT_TO_SOURCE') + ' is not a valid IPv4 address'
|
|
|
|
|
|
+ print(os.getenv('SNAT_TO_SOURCE') + ' is not a valid IPv4 address')
|
|
|
|
|
|
if os.getenv('SNAT6_TO_SOURCE') and os.getenv('SNAT6_TO_SOURCE') is not 'n':
|
|
if os.getenv('SNAT6_TO_SOURCE') and os.getenv('SNAT6_TO_SOURCE') is not 'n':
|
|
try:
|
|
try:
|
|
@@ -531,7 +531,7 @@ if __name__ == '__main__':
|
|
snat6_thread.daemon = True
|
|
snat6_thread.daemon = True
|
|
snat6_thread.start()
|
|
snat6_thread.start()
|
|
except ValueError:
|
|
except ValueError:
|
|
- print os.getenv('SNAT6_TO_SOURCE') + ' is not a valid IPv6 address'
|
|
|
|
|
|
+ print(os.getenv('SNAT6_TO_SOURCE') + ' is not a valid IPv6 address')
|
|
|
|
|
|
autopurge_thread = Thread(target=autopurge)
|
|
autopurge_thread = Thread(target=autopurge)
|
|
autopurge_thread.daemon = True
|
|
autopurge_thread.daemon = True
|