blob: 08c8727125bba694069f235f0090fc3cd6e09459 [file] [log] [blame]
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +02001#!/usr/bin/env python3
2
3import re
4import os
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02005import sys
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +02006import time
7import atexit
8import signal
9import ipaddress
10from collections import Counter
11from random import randint
12from threading import Thread
13from threading import Lock
14import redis
15import json
16import iptc
17import dns.resolver
18import dns.exception
19
20while True:
21 try:
22 redis_slaveof_ip = os.getenv('REDIS_SLAVEOF_IP', '')
23 redis_slaveof_port = os.getenv('REDIS_SLAVEOF_PORT', '')
24 if "".__eq__(redis_slaveof_ip):
25 r = redis.StrictRedis(host=os.getenv('IPV4_NETWORK', '172.22.1') + '.249', decode_responses=True, port=6379, db=0)
26 else:
27 r = redis.StrictRedis(host=redis_slaveof_ip, decode_responses=True, port=redis_slaveof_port, db=0)
28 r.ping()
29 except Exception as ex:
30 print('%s - trying again in 3 seconds' % (ex))
31 time.sleep(3)
32 else:
33 break
34
35pubsub = r.pubsub()
36
37WHITELIST = []
38BLACKLIST= []
39
40bans = {}
41
42quit_now = False
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020043exit_code = 0
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +020044lock = Lock()
45
46def log(priority, message):
47 tolog = {}
48 tolog['time'] = int(round(time.time()))
49 tolog['priority'] = priority
50 tolog['message'] = message
51 r.lpush('NETFILTER_LOG', json.dumps(tolog, ensure_ascii=False))
52 print(message)
53
54def logWarn(message):
55 log('warn', message)
56
57def logCrit(message):
58 log('crit', message)
59
60def logInfo(message):
61 log('info', message)
62
63def refreshF2boptions():
64 global f2boptions
65 global quit_now
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020066 global exit_code
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +020067 if not r.get('F2B_OPTIONS'):
68 f2boptions = {}
69 f2boptions['ban_time'] = int
70 f2boptions['max_attempts'] = int
71 f2boptions['retry_window'] = int
72 f2boptions['netban_ipv4'] = int
73 f2boptions['netban_ipv6'] = int
74 f2boptions['ban_time'] = r.get('F2B_BAN_TIME') or 1800
75 f2boptions['max_attempts'] = r.get('F2B_MAX_ATTEMPTS') or 10
76 f2boptions['retry_window'] = r.get('F2B_RETRY_WINDOW') or 600
77 f2boptions['netban_ipv4'] = r.get('F2B_NETBAN_IPV4') or 32
78 f2boptions['netban_ipv6'] = r.get('F2B_NETBAN_IPV6') or 128
79 r.set('F2B_OPTIONS', json.dumps(f2boptions, ensure_ascii=False))
80 else:
81 try:
82 f2boptions = {}
83 f2boptions = json.loads(r.get('F2B_OPTIONS'))
84 except ValueError:
85 print('Error loading F2B options: F2B_OPTIONS is not json')
86 quit_now = True
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020087 exit_code = 2
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +020088
89def refreshF2bregex():
90 global f2bregex
91 global quit_now
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020092 global exit_code
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +020093 if not r.get('F2B_REGEX'):
94 f2bregex = {}
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +010095 f2bregex[1] = 'mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)'
96 f2bregex[2] = 'Rspamd UI: Invalid password by ([0-9a-f\.:]+)'
97 f2bregex[3] = 'warning: .*\[([0-9a-f\.:]+)\]: SASL .+ authentication failed'
98 f2bregex[4] = 'warning: non-SMTP command from .*\[([0-9a-f\.:]+)]:.+'
99 f2bregex[5] = 'NOQUEUE: reject: RCPT from \[([0-9a-f\.:]+)].+Protocol error.+'
100 f2bregex[6] = '-login: Disconnected \(auth failed, .+\): user=.*, method=.+, rip=([0-9a-f\.:]+),'
101 f2bregex[7] = '-login: Aborted login \(auth failed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
102 f2bregex[8] = '-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
103 f2bregex[9] = 'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
104 f2bregex[10] = '([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200105 r.set('F2B_REGEX', json.dumps(f2bregex, ensure_ascii=False))
106 else:
107 try:
108 f2bregex = {}
109 f2bregex = json.loads(r.get('F2B_REGEX'))
110 except ValueError:
111 print('Error loading F2B options: F2B_REGEX is not json')
112 quit_now = True
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200113 exit_code = 2
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200114
115if r.exists('F2B_LOG'):
116 r.rename('F2B_LOG', 'NETFILTER_LOG')
117
118def mailcowChainOrder():
119 global lock
120 global quit_now
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200121 global exit_code
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200122 while not quit_now:
123 time.sleep(10)
124 with lock:
125 filter4_table = iptc.Table(iptc.Table.FILTER)
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100126 filter6_table = iptc.Table6(iptc.Table6.FILTER)
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200127 filter4_table.refresh()
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100128 filter6_table.refresh()
129 for f in [filter4_table, filter6_table]:
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200130 forward_chain = iptc.Chain(f, 'FORWARD')
131 input_chain = iptc.Chain(f, 'INPUT')
132 for chain in [forward_chain, input_chain]:
133 target_found = False
134 for position, item in enumerate(chain.rules):
135 if item.target.name == 'MAILCOW':
136 target_found = True
137 if position > 2:
138 logCrit('Error in %s chain order: MAILCOW on position %d, restarting container' % (chain.name, position))
139 quit_now = True
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200140 exit_code = 2
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200141 if not target_found:
142 logCrit('Error in %s chain: MAILCOW target not found, restarting container' % (chain.name))
143 quit_now = True
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200144 exit_code = 2
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200145
146def ban(address):
147 global lock
148 refreshF2boptions()
149 BAN_TIME = int(f2boptions['ban_time'])
150 MAX_ATTEMPTS = int(f2boptions['max_attempts'])
151 RETRY_WINDOW = int(f2boptions['retry_window'])
152 NETBAN_IPV4 = '/' + str(f2boptions['netban_ipv4'])
153 NETBAN_IPV6 = '/' + str(f2boptions['netban_ipv6'])
154
155 ip = ipaddress.ip_address(address)
156 if type(ip) is ipaddress.IPv6Address and ip.ipv4_mapped:
157 ip = ip.ipv4_mapped
158 address = str(ip)
159 if ip.is_private or ip.is_loopback:
160 return
161
162 self_network = ipaddress.ip_network(address)
163
164 with lock:
165 temp_whitelist = set(WHITELIST)
166
167 if temp_whitelist:
168 for wl_key in temp_whitelist:
169 wl_net = ipaddress.ip_network(wl_key, False)
170 if wl_net.overlaps(self_network):
171 logInfo('Address %s is whitelisted by rule %s' % (self_network, wl_net))
172 return
173
174 net = ipaddress.ip_network((address + (NETBAN_IPV4 if type(ip) is ipaddress.IPv4Address else NETBAN_IPV6)), strict=False)
175 net = str(net)
176
177 if not net in bans or time.time() - bans[net]['last_attempt'] > RETRY_WINDOW:
178 bans[net] = { 'attempts': 0 }
179 active_window = RETRY_WINDOW
180 else:
181 active_window = time.time() - bans[net]['last_attempt']
182
183 bans[net]['attempts'] += 1
184 bans[net]['last_attempt'] = time.time()
185
186 active_window = time.time() - bans[net]['last_attempt']
187
188 if bans[net]['attempts'] >= MAX_ATTEMPTS:
189 cur_time = int(round(time.time()))
190 logCrit('Banning %s for %d minutes' % (net, BAN_TIME / 60))
191 if type(ip) is ipaddress.IPv4Address:
192 with lock:
193 chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
194 rule = iptc.Rule()
195 rule.src = net
196 target = iptc.Target(rule, "REJECT")
197 rule.target = target
198 if rule not in chain.rules:
199 chain.insert_rule(rule)
200 else:
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100201 with lock:
202 chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), 'MAILCOW')
203 rule = iptc.Rule6()
204 rule.src = net
205 target = iptc.Target(rule, "REJECT")
206 rule.target = target
207 if rule not in chain.rules:
208 chain.insert_rule(rule)
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200209 r.hset('F2B_ACTIVE_BANS', '%s' % net, cur_time + BAN_TIME)
210 else:
211 logWarn('%d more attempts in the next %d seconds until %s is banned' % (MAX_ATTEMPTS - bans[net]['attempts'], RETRY_WINDOW, net))
212
213def unban(net):
214 global lock
215 if not net in bans:
216 logInfo('%s is not banned, skipping unban and deleting from queue (if any)' % net)
217 r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
218 return
219 logInfo('Unbanning %s' % net)
220 if type(ipaddress.ip_network(net)) is ipaddress.IPv4Network:
221 with lock:
222 chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
223 rule = iptc.Rule()
224 rule.src = net
225 target = iptc.Target(rule, "REJECT")
226 rule.target = target
227 if rule in chain.rules:
228 chain.delete_rule(rule)
229 else:
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100230 with lock:
231 chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), 'MAILCOW')
232 rule = iptc.Rule6()
233 rule.src = net
234 target = iptc.Target(rule, "REJECT")
235 rule.target = target
236 if rule in chain.rules:
237 chain.delete_rule(rule)
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200238 r.hdel('F2B_ACTIVE_BANS', '%s' % net)
239 r.hdel('F2B_QUEUE_UNBAN', '%s' % net)
240 if net in bans:
241 del bans[net]
242
243def permBan(net, unban=False):
244 global lock
245 if type(ipaddress.ip_network(net, strict=False)) is ipaddress.IPv4Network:
246 with lock:
247 chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), 'MAILCOW')
248 rule = iptc.Rule()
249 rule.src = net
250 target = iptc.Target(rule, "REJECT")
251 rule.target = target
252 if rule not in chain.rules and not unban:
253 logCrit('Add host/network %s to blacklist' % net)
254 chain.insert_rule(rule)
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100255 r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200256 elif rule in chain.rules and unban:
257 logCrit('Remove host/network %s from blacklist' % net)
258 chain.delete_rule(rule)
259 r.hdel('F2B_PERM_BANS', '%s' % net)
260 else:
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100261 with lock:
262 chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), 'MAILCOW')
263 rule = iptc.Rule6()
264 rule.src = net
265 target = iptc.Target(rule, "REJECT")
266 rule.target = target
267 if rule not in chain.rules and not unban:
268 logCrit('Add host/network %s to blacklist' % net)
269 chain.insert_rule(rule)
270 r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
271 elif rule in chain.rules and unban:
272 logCrit('Remove host/network %s from blacklist' % net)
273 chain.delete_rule(rule)
274 r.hdel('F2B_PERM_BANS', '%s' % net)
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200275
276def quit(signum, frame):
277 global quit_now
278 quit_now = True
279
280def clear():
281 global lock
282 logInfo('Clearing all bans')
283 for net in bans.copy():
284 unban(net)
285 with lock:
286 filter4_table = iptc.Table(iptc.Table.FILTER)
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100287 filter6_table = iptc.Table6(iptc.Table6.FILTER)
288 for filter_table in [filter4_table, filter6_table]:
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200289 filter_table.autocommit = False
290 forward_chain = iptc.Chain(filter_table, "FORWARD")
291 input_chain = iptc.Chain(filter_table, "INPUT")
292 mailcow_chain = iptc.Chain(filter_table, "MAILCOW")
293 if mailcow_chain in filter_table.chains:
294 for rule in mailcow_chain.rules:
295 mailcow_chain.delete_rule(rule)
296 for rule in forward_chain.rules:
297 if rule.target.name == 'MAILCOW':
298 forward_chain.delete_rule(rule)
299 for rule in input_chain.rules:
300 if rule.target.name == 'MAILCOW':
301 input_chain.delete_rule(rule)
302 filter_table.delete_chain("MAILCOW")
303 filter_table.commit()
304 filter_table.refresh()
305 filter_table.autocommit = True
306 r.delete('F2B_ACTIVE_BANS')
307 r.delete('F2B_PERM_BANS')
308 pubsub.unsubscribe()
309
310def watch():
311 logInfo('Watching Redis channel F2B_CHANNEL')
312 pubsub.subscribe('F2B_CHANNEL')
313
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200314 global quit_now
315 global exit_code
316
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200317 while not quit_now:
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200318 try:
319 for item in pubsub.listen():
320 refreshF2bregex()
321 for rule_id, rule_regex in f2bregex.items():
322 if item['data'] and item['type'] == 'message':
323 try:
324 result = re.search(rule_regex, item['data'])
325 except re.error:
326 result = False
327 if result:
328 addr = result.group(1)
329 ip = ipaddress.ip_address(addr)
330 if ip.is_private or ip.is_loopback:
331 continue
332 logWarn('%s matched rule id %s (%s)' % (addr, rule_id, item['data']))
333 ban(addr)
334 except Exception as ex:
335 logWarn('Error reading log line from pubsub')
336 quit_now = True
337 exit_code = 2
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200338
339def snat4(snat_target):
340 global lock
341 global quit_now
342
343 def get_snat4_rule():
344 rule = iptc.Rule()
345 rule.src = os.getenv('IPV4_NETWORK', '172.22.1') + '.0/24'
346 rule.dst = '!' + rule.src
347 target = rule.create_target("SNAT")
348 target.to_source = snat_target
349 return rule
350
351 while not quit_now:
352 time.sleep(10)
353 with lock:
354 try:
355 table = iptc.Table('nat')
356 table.refresh()
357 chain = iptc.Chain(table, 'POSTROUTING')
358 table.autocommit = False
359 if get_snat4_rule() not in chain.rules:
360 logCrit('Added POSTROUTING rule for source network %s to SNAT target %s' % (get_snat4_rule().src, snat_target))
361 chain.insert_rule(get_snat4_rule())
362 table.commit()
363 else:
364 for position, item in enumerate(chain.rules):
365 if item == get_snat4_rule():
366 if position != 0:
367 chain.delete_rule(get_snat4_rule())
368 table.commit()
369 table.autocommit = True
370 except:
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100371 print('Error running SNAT4, retrying...')
372
373def snat6(snat_target):
374 global lock
375 global quit_now
376
377 def get_snat6_rule():
378 rule = iptc.Rule6()
379 rule.src = os.getenv('IPV6_NETWORK', 'fd4d:6169:6c63:6f77::/64')
380 rule.dst = '!' + rule.src
381 target = rule.create_target("SNAT")
382 target.to_source = snat_target
383 return rule
384
385 while not quit_now:
386 time.sleep(10)
387 with lock:
388 try:
389 table = iptc.Table6('nat')
390 table.refresh()
391 chain = iptc.Chain(table, 'POSTROUTING')
392 table.autocommit = False
393 if get_snat6_rule() not in chain.rules:
394 logInfo('Added POSTROUTING rule for source network %s to SNAT target %s' % (get_snat6_rule().src, snat_target))
395 chain.insert_rule(get_snat6_rule())
396 table.commit()
397 else:
398 for position, item in enumerate(chain.rules):
399 if item == get_snat6_rule():
400 if position != 0:
401 chain.delete_rule(get_snat6_rule())
402 table.commit()
403 table.autocommit = True
404 except:
405 print('Error running SNAT6, retrying...')
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200406
407def autopurge():
408 while not quit_now:
409 time.sleep(10)
410 refreshF2boptions()
411 BAN_TIME = int(f2boptions['ban_time'])
412 MAX_ATTEMPTS = int(f2boptions['max_attempts'])
413 QUEUE_UNBAN = r.hgetall('F2B_QUEUE_UNBAN')
414 if QUEUE_UNBAN:
415 for net in QUEUE_UNBAN:
416 unban(str(net))
417 for net in bans.copy():
418 if bans[net]['attempts'] >= MAX_ATTEMPTS:
419 if time.time() - bans[net]['last_attempt'] > BAN_TIME:
420 unban(net)
421
422def isIpNetwork(address):
423 try:
424 ipaddress.ip_network(address, False)
425 except ValueError:
426 return False
427 return True
428
429
430def genNetworkList(list):
431 resolver = dns.resolver.Resolver()
432 hostnames = []
433 networks = []
434 for key in list:
435 if isIpNetwork(key):
436 networks.append(key)
437 else:
438 hostnames.append(key)
439 for hostname in hostnames:
440 hostname_ips = []
441 for rdtype in ['A', 'AAAA']:
442 try:
443 answer = resolver.resolve(qname=hostname, rdtype=rdtype, lifetime=3)
444 except dns.exception.Timeout:
445 logInfo('Hostname %s timedout on resolve' % hostname)
446 break
447 except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
448 continue
449 except dns.exception.DNSException as dnsexception:
450 logInfo('%s' % dnsexception)
451 continue
452 for rdata in answer:
453 hostname_ips.append(rdata.to_text())
454 networks.extend(hostname_ips)
455 return set(networks)
456
457def whitelistUpdate():
458 global lock
459 global quit_now
460 global WHITELIST
461 while not quit_now:
462 start_time = time.time()
463 list = r.hgetall('F2B_WHITELIST')
464 new_whitelist = []
465 if list:
466 new_whitelist = genNetworkList(list)
467 with lock:
468 if Counter(new_whitelist) != Counter(WHITELIST):
469 WHITELIST = new_whitelist
470 logInfo('Whitelist was changed, it has %s entries' % len(WHITELIST))
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100471 time.sleep(60.0 - ((time.time() - start_time) % 60.0))
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200472
473def blacklistUpdate():
474 global quit_now
475 global BLACKLIST
476 while not quit_now:
477 start_time = time.time()
478 list = r.hgetall('F2B_BLACKLIST')
479 new_blacklist = []
480 if list:
481 new_blacklist = genNetworkList(list)
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100482 if Counter(new_blacklist) != Counter(BLACKLIST):
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200483 addban = set(new_blacklist).difference(BLACKLIST)
484 delban = set(BLACKLIST).difference(new_blacklist)
485 BLACKLIST = new_blacklist
486 logInfo('Blacklist was changed, it has %s entries' % len(BLACKLIST))
487 if addban:
488 for net in addban:
489 permBan(net=net)
490 if delban:
491 for net in delban:
492 permBan(net=net, unban=True)
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100493 time.sleep(60.0 - ((time.time() - start_time) % 60.0))
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200494
495def initChain():
496 # Is called before threads start, no locking
497 print("Initializing mailcow netfilter chain")
498 # IPv4
499 if not iptc.Chain(iptc.Table(iptc.Table.FILTER), "MAILCOW") in iptc.Table(iptc.Table.FILTER).chains:
500 iptc.Table(iptc.Table.FILTER).create_chain("MAILCOW")
501 for c in ['FORWARD', 'INPUT']:
502 chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), c)
503 rule = iptc.Rule()
504 rule.src = '0.0.0.0/0'
505 rule.dst = '0.0.0.0/0'
506 target = iptc.Target(rule, "MAILCOW")
507 rule.target = target
508 if rule not in chain.rules:
509 chain.insert_rule(rule)
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100510 # IPv6
511 if not iptc.Chain(iptc.Table6(iptc.Table6.FILTER), "MAILCOW") in iptc.Table6(iptc.Table6.FILTER).chains:
512 iptc.Table6(iptc.Table6.FILTER).create_chain("MAILCOW")
513 for c in ['FORWARD', 'INPUT']:
514 chain = iptc.Chain(iptc.Table6(iptc.Table6.FILTER), c)
515 rule = iptc.Rule6()
516 rule.src = '::/0'
517 rule.dst = '::/0'
518 target = iptc.Target(rule, "MAILCOW")
519 rule.target = target
520 if rule not in chain.rules:
521 chain.insert_rule(rule)
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200522
523if __name__ == '__main__':
524
525 # In case a previous session was killed without cleanup
526 clear()
527 # Reinit MAILCOW chain
528 initChain()
529
530 watch_thread = Thread(target=watch)
531 watch_thread.daemon = True
532 watch_thread.start()
533
534 if os.getenv('SNAT_TO_SOURCE') and os.getenv('SNAT_TO_SOURCE') != 'n':
535 try:
536 snat_ip = os.getenv('SNAT_TO_SOURCE')
537 snat_ipo = ipaddress.ip_address(snat_ip)
538 if type(snat_ipo) is ipaddress.IPv4Address:
539 snat4_thread = Thread(target=snat4,args=(snat_ip,))
540 snat4_thread.daemon = True
541 snat4_thread.start()
542 except ValueError:
543 print(os.getenv('SNAT_TO_SOURCE') + ' is not a valid IPv4 address')
544
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +0100545 if os.getenv('SNAT6_TO_SOURCE') and os.getenv('SNAT6_TO_SOURCE') != 'n':
546 try:
547 snat_ip = os.getenv('SNAT6_TO_SOURCE')
548 snat_ipo = ipaddress.ip_address(snat_ip)
549 if type(snat_ipo) is ipaddress.IPv6Address:
550 snat6_thread = Thread(target=snat6,args=(snat_ip,))
551 snat6_thread.daemon = True
552 snat6_thread.start()
553 except ValueError:
554 print(os.getenv('SNAT6_TO_SOURCE') + ' is not a valid IPv6 address')
555
Matthias Andreas Benkard8bb60d02021-08-16 10:41:15 +0200556 autopurge_thread = Thread(target=autopurge)
557 autopurge_thread.daemon = True
558 autopurge_thread.start()
559
560 mailcowchainwatch_thread = Thread(target=mailcowChainOrder)
561 mailcowchainwatch_thread.daemon = True
562 mailcowchainwatch_thread.start()
563
564 blacklistupdate_thread = Thread(target=blacklistUpdate)
565 blacklistupdate_thread.daemon = True
566 blacklistupdate_thread.start()
567
568 whitelistupdate_thread = Thread(target=whitelistUpdate)
569 whitelistupdate_thread.daemon = True
570 whitelistupdate_thread.start()
571
572 signal.signal(signal.SIGTERM, quit)
573 atexit.register(clear)
574
575 while not quit_now:
576 time.sleep(0.5)
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200577
578 sys.exit(exit_code)