resolving whitelist every minute

master
Kraeutergarten 2019-05-19 09:48:10 +02:00
parent 51f5f66c91
commit 5ed113c47f
1 changed files with 76 additions and 51 deletions

View File

@ -6,6 +6,7 @@ import time
import atexit import atexit
import signal import signal
import ipaddress import ipaddress
from collections import Counter
from random import randint from random import randint
from threading import Thread from threading import Thread
from threading import Lock from threading import Lock
@ -38,18 +39,13 @@ RULES[5] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)'
RULES[6] = '([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+' RULES[6] = '([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
#RULES[7] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+' #RULES[7] = '-login: Aborted login \(no auth .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
WHITELIST = []
bans = {} bans = {}
log = {} log = {}
quit_now = False quit_now = False
lock = Lock() lock = Lock()
def is_ip_network(address):
try:
ipaddress.ip_network(address.decode('ascii'), False)
except ValueError:
return False
return True
def refreshF2boptions(): def refreshF2boptions():
global f2boptions global f2boptions
global quit_now global quit_now
@ -118,7 +114,6 @@ def ban(address):
RETRY_WINDOW = int(f2boptions['retry_window']) RETRY_WINDOW = int(f2boptions['retry_window'])
NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4']) NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
NETBAN_IPV6 = '/' + str(f2boptions['netban_ipv6']) NETBAN_IPV6 = '/' + str(f2boptions['netban_ipv6'])
WHITELIST = r.hgetall('F2B_WHITELIST')
ip = ipaddress.ip_address(address.decode('ascii')) ip = ipaddress.ip_address(address.decode('ascii'))
if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped: if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped:
@ -128,50 +123,9 @@ def ban(address):
return return
self_network = ipaddress.ip_network(address.decode('ascii')) self_network = ipaddress.ip_network(address.decode('ascii'))
if WHITELIST: if WHITELIST:
wl_hostnames=[]
wl_networks=[]
for wl_key in WHITELIST: for wl_key in WHITELIST:
if is_ip_network(wl_key):
wl_networks.append(wl_key)
else:
wl_hostnames.append(wl_key)
for w1_hostname in wl_hostnames:
hostname_ips = []
for rdtype in ['A', 'AAAA']:
try:
answer = resolver.query(qname=w1_hostname, rdtype=rdtype, lifetime=1)
except dns.exception.Timeout as timout:
log['time'] = int(round(time.time()))
log['priority'] = 'info'
log['message'] = 'Hostname %s timedout on resolve' % (w1_hostname)
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
print 'Hostname %s timedout on resolve' % (w1_hostname)
break
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
continue
except dns.exception.DNSException as dnsexception:
log['time'] = int(round(time.time()))
log['priority'] = 'info'
log['message'] = '%s' % (dnsexception)
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
print '%s' % (dnsexception)
continue
for rdata in answer:
hostname_ips.append(rdata.to_text())
wl_networks.extend(hostname_ips)
log['time'] = int(round(time.time()))
log['priority'] = 'info'
log['message'] = 'Hostname %s is resolved to %s' % (w1_hostname, hostname_ips)
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
print 'Hostname %s is resolved to %s' % (w1_hostname, hostname_ips)
for wl_key in wl_networks:
wl_net = ipaddress.ip_network(wl_key.decode('ascii'), False) wl_net = ipaddress.ip_network(wl_key.decode('ascii'), False)
if wl_net.overlaps(self_network): if wl_net.overlaps(self_network):
@ -419,6 +373,73 @@ def autopurge():
if time.time() - bans[net]['last_attempt'] > BAN_TIME: if time.time() - bans[net]['last_attempt'] > BAN_TIME:
unban(net) unban(net)
def isIpNetwork(address):
try:
ipaddress.ip_network(address.decode('ascii'), False)
except ValueError:
return False
return True
def genNetworkList(list):
hostnames = []
networks = []
for key in list:
if isIpNetwork(key):
networks.append(key.encode("utf-8"))
else:
hostnames.append(key)
for hostname in hostnames:
hostname_ips = []
for rdtype in ['A', 'AAAA']:
try:
answer = resolver.query(qname=hostname, rdtype=rdtype, lifetime=10)
except dns.exception.Timeout:
log['time'] = int(round(time.time()))
log['priority'] = 'info'
log['message'] = 'Hostname %s timedout on resolve' % (hostname)
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
print 'Hostname %s timedout on resolve' % (hostname)
break
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
continue
except dns.exception.DNSException as dnsexception:
log['time'] = int(round(time.time()))
log['priority'] = 'info'
log['message'] = '%s' % (dnsexception)
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
print '%s' % (dnsexception)
continue
for rdata in answer:
hostname_ips.append(rdata.to_text().encode("utf-8"))
networks.extend(hostname_ips)
return networks
def whitelistUpdate():
global lock
global quit_now
global WHITELIST
while not quit_now:
start_time = time.time()
list = r.hgetall('F2B_WHITELIST')
if list:
new_whitelist = genNetworkList(list)
if Counter(new_whitelist) != Counter(WHITELIST):
WHITELIST = new_whitelist
log['time'] = int(round(time.time()))
log['priority'] = 'info'
log['message'] = 'New entrys for whitelist %s' % (WHITELIST)
r.lpush('NETFILTER_LOG', json.dumps(log, ensure_ascii=False))
print 'New entrys for whitelist %s' % (WHITELIST)
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"
@ -520,6 +541,10 @@ if __name__ == '__main__':
mailcowchainwatch_thread.daemon = True mailcowchainwatch_thread.daemon = True
mailcowchainwatch_thread.start() mailcowchainwatch_thread.start()
whitelistupdate_thread = Thread(target=whitelistUpdate)
whitelistupdate_thread.daemon = True
whitelistupdate_thread.start()
signal.signal(signal.SIGTERM, quit) signal.signal(signal.SIGTERM, quit)
atexit.register(clear) atexit.register(clear)