2121from modules .NFTables import NFTables
2222
2323
24- # connect to redis
25- while True :
26- try :
27- redis_slaveof_ip = os .getenv ('REDIS_SLAVEOF_IP' , '' )
28- redis_slaveof_port = os .getenv ('REDIS_SLAVEOF_PORT' , '' )
29- if "" .__eq__ (redis_slaveof_ip ):
30- r = redis .StrictRedis (host = os .getenv ('IPV4_NETWORK' , '172.22.1' ) + '.249' , decode_responses = True , port = 6379 , db = 0 )
31- else :
32- r = redis .StrictRedis (host = redis_slaveof_ip , decode_responses = True , port = redis_slaveof_port , db = 0 )
33- r .ping ()
34- except Exception as ex :
35- print ('%s - trying again in 3 seconds' % (ex ))
36- time .sleep (3 )
37- else :
38- break
39- pubsub = r .pubsub ()
40-
41- # rename fail2ban to netfilter
42- if r .exists ('F2B_LOG' ):
43- r .rename ('F2B_LOG' , 'NETFILTER_LOG' )
44-
45-
4624# globals
4725WHITELIST = []
4826BLACKLIST = []
4927bans = {}
5028quit_now = False
5129exit_code = 0
5230lock = Lock ()
53-
54-
55- # init Logger
56- logger = Logger (r )
57- # init backend
58- backend = sys .argv [1 ]
59- if backend == "nftables" :
60- logger .logInfo ('Using NFTables backend' )
61- tables = NFTables ("MAILCOW" , logger )
62- else :
63- logger .logInfo ('Using IPTables backend' )
64- tables = IPTables ("MAILCOW" , logger )
31+ chain_name = "MAILCOW"
32+ r = None
33+ pubsub = None
34+ clear_before_quit = False
6535
6636
6737def refreshF2boptions ():
@@ -250,17 +220,21 @@ def clear():
250220 with lock :
251221 tables .clearIPv4Table ()
252222 tables .clearIPv6Table ()
253- r .delete ('F2B_ACTIVE_BANS' )
254- r .delete ('F2B_PERM_BANS' )
255- pubsub .unsubscribe ()
223+ try :
224+ if r is not None :
225+ r .delete ('F2B_ACTIVE_BANS' )
226+ r .delete ('F2B_PERM_BANS' )
227+ except Exception as ex :
228+ logger .logWarn ('Error clearing redis keys F2B_ACTIVE_BANS and F2B_PERM_BANS: %s' % ex )
256229
257230def watch ():
258- logger .logInfo ('Watching Redis channel F2B_CHANNEL' )
259- pubsub .subscribe ('F2B_CHANNEL' )
260-
231+ global pubsub
261232 global quit_now
262233 global exit_code
263234
235+ logger .logInfo ('Watching Redis channel F2B_CHANNEL' )
236+ pubsub .subscribe ('F2B_CHANNEL' )
237+
264238 while not quit_now :
265239 try :
266240 for item in pubsub .listen ():
@@ -280,6 +254,7 @@ def watch():
280254 ban (addr )
281255 except Exception as ex :
282256 logger .logWarn ('Error reading log line from pubsub: %s' % ex )
257+ pubsub = None
283258 quit_now = True
284259 exit_code = 2
285260
@@ -403,21 +378,76 @@ def blacklistUpdate():
403378 permBan (net = net , unban = True )
404379 time .sleep (60.0 - ((time .time () - start_time ) % 60.0 ))
405380
406- def quit (signum , frame ):
407- global quit_now
408- quit_now = True
381+ def sigterm_quit (signum , frame ):
382+ global clear_before_quit
383+ clear_before_quit = True
384+ sys .exit (exit_code )
385+
386+ def berfore_quit ():
387+ if clear_before_quit :
388+ clear ()
389+ if pubsub is not None :
390+ pubsub .unsubscribe ()
409391
410392
411393if __name__ == '__main__' :
412- refreshF2boptions ()
394+ atexit .register (berfore_quit )
395+ signal .signal (signal .SIGTERM , sigterm_quit )
396+
397+ # init Logger
398+ logger = Logger (None )
399+
400+ # init backend
401+ backend = sys .argv [1 ]
402+ if backend == "nftables" :
403+ logger .logInfo ('Using NFTables backend' )
404+ tables = NFTables (chain_name , logger )
405+ else :
406+ logger .logInfo ('Using IPTables backend' )
407+ tables = IPTables (chain_name , logger )
408+
413409 # In case a previous session was killed without cleanup
414410 clear ()
411+
415412 # Reinit MAILCOW chain
416413 # Is called before threads start, no locking
417414 logger .logInfo ("Initializing mailcow netfilter chain" )
418415 tables .initChainIPv4 ()
419416 tables .initChainIPv6 ()
420417
418+ if os .getenv ("DISABLE_NETFILTER_ISOLATION_RULE" ).lower () in ("y" , "yes" ):
419+ logger .logInfo (f"Skipping { chain_name } isolation" )
420+ else :
421+ logger .logInfo (f"Setting { chain_name } isolation" )
422+ tables .create_mailcow_isolation_rule ("br-mailcow" , [3306 , 6379 , 8983 , 12345 ], os .getenv ("MAILCOW_REPLICA_IP" ))
423+
424+ # connect to redis
425+ while True :
426+ try :
427+ redis_slaveof_ip = os .getenv ('REDIS_SLAVEOF_IP' , '' )
428+ redis_slaveof_port = os .getenv ('REDIS_SLAVEOF_PORT' , '' )
429+ if "" .__eq__ (redis_slaveof_ip ):
430+ r = redis .StrictRedis (host = os .getenv ('IPV4_NETWORK' , '172.22.1' ) + '.249' , decode_responses = True , port = 6379 , db = 0 )
431+ else :
432+ r = redis .StrictRedis (host = redis_slaveof_ip , decode_responses = True , port = redis_slaveof_port , db = 0 )
433+ r .ping ()
434+ pubsub = r .pubsub ()
435+ except Exception as ex :
436+ print ('%s - trying again in 3 seconds' % (ex ))
437+ time .sleep (3 )
438+ else :
439+ break
440+ Logger .r = r
441+
442+ # rename fail2ban to netfilter
443+ if r .exists ('F2B_LOG' ):
444+ r .rename ('F2B_LOG' , 'NETFILTER_LOG' )
445+ # clear bans in redis
446+ r .delete ('F2B_ACTIVE_BANS' )
447+ r .delete ('F2B_PERM_BANS' )
448+
449+ refreshF2boptions ()
450+
421451 watch_thread = Thread (target = watch )
422452 watch_thread .daemon = True
423453 watch_thread .start ()
@@ -460,9 +490,6 @@ def quit(signum, frame):
460490 whitelistupdate_thread .daemon = True
461491 whitelistupdate_thread .start ()
462492
463- signal .signal (signal .SIGTERM , quit )
464- atexit .register (clear )
465-
466493 while not quit_now :
467494 time .sleep (0.5 )
468495
0 commit comments