2022-06-15 09:11:27 +02:00
#!/usr/bin/env python3
2024-04-21 17:17:43 +02:00
from __future__ import annotations
2023-04-12 11:04:37 +02:00
import argparse , sys , platform , os , multiprocessing , subprocess , getpass
2022-06-15 09:11:27 +02:00
2022-06-19 20:11:28 +02:00
pref = " \033 [ "
reset = f " { pref } 0m "
2024-07-04 14:31:41 +02:00
class g :
composefile = " firegex-compose-tmp-file.yml "
2024-04-21 16:05:06 +02:00
os . chdir ( os . path . dirname ( os . path . realpath ( __file__ ) ) )
2024-04-21 17:17:43 +02:00
#Terminal colors
2022-06-19 20:11:28 +02:00
class colors :
black = " 30m "
red = " 31m "
green = " 32m "
yellow = " 33m "
blue = " 34m "
magenta = " 35m "
cyan = " 36m "
white = " 37m "
def puts ( text , * args , color = colors . white , is_bold = False , * * kwargs ) :
print ( f ' { pref } { 1 if is_bold else 0 } ; { color } ' + text + reset , * args , * * kwargs )
def sep ( ) : puts ( " ----------------------------------- " , is_bold = True )
2022-08-09 10:28:30 +00:00
2024-07-04 14:31:41 +02:00
def check_if_exists ( program , get_output = False ) :
if get_output :
return subprocess . getoutput ( program )
return subprocess . call ( program , stdout = subprocess . DEVNULL , stderr = subprocess . STDOUT , shell = True ) == 0
2023-04-11 21:15:30 +02:00
def composecmd ( cmd , composefile = None ) :
if composefile :
cmd = f " -f { composefile } { cmd } "
if not check_if_exists ( " docker ps " ) :
return puts ( " Cannot use docker, the user hasn ' t the permission or docker isn ' t running " , color = colors . red )
elif check_if_exists ( " docker compose " ) :
2023-04-24 18:03:03 +02:00
return os . system ( f " docker compose -p firegex { cmd } " )
2023-04-11 21:15:30 +02:00
elif check_if_exists ( " docker-compose " ) :
2023-04-24 18:03:03 +02:00
return os . system ( f " docker-compose -p firegex { cmd } " )
2023-04-11 21:15:30 +02:00
else :
puts ( " Docker compose not found! please install docker compose! " , color = colors . red )
2022-08-23 21:56:33 +00:00
def dockercmd ( cmd ) :
2023-04-11 21:15:30 +02:00
if check_if_exists ( " docker " ) :
return os . system ( f " docker { cmd } " )
elif not check_if_exists ( " docker ps " ) :
puts ( " Cannot use docker, the user hasn ' t the permission or docker isn ' t running " , color = colors . red )
else :
puts ( " Docker not found! please install docker! " , color = colors . red )
2024-07-04 14:31:41 +02:00
2024-04-21 17:42:03 +02:00
def check_already_running ( ) :
2024-07-04 14:31:41 +02:00
return " firegex " in check_if_exists ( f ' docker ps --filter " name=^firegex$ " ' , get_output = True )
2024-04-21 17:42:03 +02:00
2024-04-22 01:06:06 +02:00
def gen_args ( args_to_parse : list [ str ] | None = None ) :
2024-04-21 17:17:43 +02:00
#Main parser
parser = argparse . ArgumentParser ( description = " Firegex Manager " )
if os . path . isfile ( " ./Dockerfile " ) :
parser . add_argument ( ' --build ' , " -b " , dest = " bef_build " , required = False , action = " store_true " , help = ' Build the container from source ' , default = False )
2024-04-22 01:06:06 +02:00
parser . add_argument ( ' --clear ' , dest = " bef_clear " , required = False , action = " store_true " , help = ' Delete docker volume associated to firegex resetting all the settings ' , default = False )
2022-08-23 21:56:33 +00:00
2024-04-21 16:05:06 +02:00
subcommands = parser . add_subparsers ( dest = " command " , help = " Command to execute [Default start if not running] " )
#Compose Command
parser_compose = subcommands . add_parser ( ' compose ' , help = ' Run docker compose command ' )
2024-05-11 16:24:18 +02:00
parser_compose . add_argument ( ' compose_args ' , nargs = argparse . REMAINDER , help = ' Arguments to pass to docker compose ' , default = [ ] )
2024-04-21 16:05:06 +02:00
#Start Command
parser_start = subcommands . add_parser ( ' start ' , help = ' Start the firewall ' )
parser_start . add_argument ( ' --threads ' , " -t " , type = int , required = False , help = ' Number of threads started for each service/utility ' , default = - 1 )
parser_start . add_argument ( ' --psw-no-interactive ' , type = str , required = False , help = ' Password for no-interactive mode ' , default = None )
parser_start . add_argument ( ' --startup-psw ' , ' -P ' , required = False , action = " store_true " , help = ' Insert password in the startup screen of firegex ' , default = False )
parser_start . add_argument ( ' --port ' , " -p " , type = int , required = False , help = ' Port where open the web service of the firewall ' , default = 4444 )
parser_start . add_argument ( ' --logs ' , required = False , action = " store_true " , help = ' Show firegex logs ' , default = False )
2024-04-21 17:17:43 +02:00
if os . path . isfile ( " ./Dockerfile " ) :
parser_start . add_argument ( ' --build ' , " -b " , required = False , action = " store_true " , help = ' Build the container from source ' , default = False )
2024-04-21 16:05:06 +02:00
#Stop Command
parser_stop = subcommands . add_parser ( ' stop ' , help = ' Stop the firewall ' )
parser_stop . add_argument ( ' --clear ' , required = False , action = " store_true " , help = ' Delete docker volume associated to firegex resetting all the settings ' , default = False )
parser_restart = subcommands . add_parser ( ' restart ' , help = ' Restart the firewall ' )
parser_restart . add_argument ( ' --logs ' , required = False , action = " store_true " , help = ' Show firegex logs ' , default = False )
2024-04-22 01:06:06 +02:00
args = parser . parse_args ( args = args_to_parse )
2024-04-21 16:05:06 +02:00
2024-04-21 17:45:06 +02:00
if not " clear " in args :
args . clear = False
2024-04-21 17:17:43 +02:00
if not " threads " in args or args . threads < 1 :
args . threads = multiprocessing . cpu_count ( )
if not " port " in args or args . port < 1 :
args . port = 4444
if not " bef_build " in args :
args . bef_build = False
if not " build " in args :
args . build = False
2024-04-21 17:45:06 +02:00
if args . command is None :
2024-04-22 01:06:06 +02:00
if not args . clear :
return gen_args ( [ " start " , * sys . argv [ 1 : ] ] )
2024-04-21 17:17:43 +02:00
args . build = args . bef_build or args . build
2024-04-22 01:06:06 +02:00
args . clear = args . bef_clear or args . clear
2024-04-21 17:17:43 +02:00
return args
2023-04-11 21:15:30 +02:00
2024-04-21 16:05:06 +02:00
args = gen_args ( )
def is_linux ( ) :
return " linux " in sys . platform and not ' microsoft-standard ' in platform . uname ( ) . release
def write_compose ( skip_password = True ) :
psw_set = get_password ( ) if not skip_password else None
2024-07-04 14:31:41 +02:00
with open ( g . composefile , " wt " ) as compose :
2023-04-11 22:15:17 +02:00
2024-04-21 16:05:06 +02:00
if is_linux ( ) : #Check if not is a wsl also
2023-04-12 23:53:43 +02:00
compose . write ( f """
2022-06-15 09:11:27 +02:00
services :
firewall :
restart : unless - stopped
2023-04-12 12:05:03 +02:00
container_name : firegex
2024-04-09 04:01:18 +02:00
{ " build: . " if args . build else " image: ghcr.io/pwnzer0tt1/firegex " }
2022-06-15 09:11:27 +02:00
network_mode : " host "
environment :
2022-06-28 13:26:06 +02:00
- PORT = { args . port }
2022-07-22 00:34:57 +02:00
- NTHREADS = { args . threads }
2022-08-09 10:28:30 +00:00
{ " - HEX_SET_PSW= " + psw_set . encode ( ) . hex ( ) if psw_set else " " }
2022-06-15 09:11:27 +02:00
volumes :
2023-04-24 18:03:03 +02:00
- firegex_data : / execute / db
2023-04-12 23:53:43 +02:00
- type : bind
source : / proc / sys / net / ipv4 / conf / all / route_localnet
target : / sys_host / net . ipv4 . conf . all . route_localnet
- type : bind
source : / proc / sys / net / ipv4 / ip_forward
target : / sys_host / net . ipv4 . ip_forward
- type : bind
source : / proc / sys / net / ipv4 / conf / all / forwarding
target : / sys_host / net . ipv4 . conf . all . forwarding
- type : bind
source : / proc / sys / net / ipv6 / conf / all / forwarding
target : / sys_host / net . ipv6 . conf . all . forwarding
2022-07-07 09:45:27 +02:00
cap_add :
- NET_ADMIN
2023-04-24 18:03:03 +02:00
volumes :
firegex_data :
2022-06-15 09:11:27 +02:00
""" )
2022-07-19 15:17:34 +02:00
2023-04-12 23:53:43 +02:00
else :
compose . write ( f """
2022-06-15 09:11:27 +02:00
services :
firewall :
restart : unless - stopped
2023-04-12 12:05:03 +02:00
container_name : firegex
2023-04-11 22:15:17 +02:00
{ " build: . " if args . build else " image: ghcr.io/pwnzer0tt1/firegex " }
2022-06-15 09:11:27 +02:00
ports :
- { args . port } : { args . port }
environment :
2022-06-28 13:26:06 +02:00
- PORT = { args . port }
2022-07-22 00:34:57 +02:00
- NTHREADS = { args . threads }
2022-08-09 10:28:30 +00:00
{ " - HEX_SET_PSW= " + psw_set . encode ( ) . hex ( ) if psw_set else " " }
2022-06-15 09:11:27 +02:00
volumes :
2023-04-24 18:03:03 +02:00
- firegex_data : / execute / db
2022-07-07 09:45:27 +02:00
cap_add :
- NET_ADMIN
2023-04-24 18:03:03 +02:00
volumes :
firegex_data :
2022-06-15 09:11:27 +02:00
""" )
2023-04-24 18:03:03 +02:00
2024-04-21 17:42:03 +02:00
2024-04-21 16:05:06 +02:00
def get_password ( ) :
if volume_exists ( ) or args . startup_psw :
return None
if args . psw_no_interactive :
return args . psw_no_interactive
psw_set = None
while True :
while True :
puts ( " Insert a password for firegex: " , end = " " , color = colors . yellow , is_bold = True , flush = True )
psw_set = getpass . getpass ( " " )
if ( len ( psw_set ) < 8 ) :
puts ( " The password has to be at least 8 char long " , color = colors . red , is_bold = True , flush = True )
else :
break
puts ( " Confirm the password: " , end = " " , color = colors . yellow , is_bold = True , flush = True )
check = getpass . getpass ( " " )
if check != psw_set :
puts ( " Passwords don ' t match! " , color = colors . red , is_bold = True , flush = True )
else :
break
return psw_set
2023-04-24 18:03:03 +02:00
2024-07-04 14:31:41 +02:00
2024-04-21 16:05:06 +02:00
def volume_exists ( ) :
2024-07-04 14:31:41 +02:00
return " firegex_firegex_data " in check_if_exists ( f ' docker volume ls --filter " name=^firegex_firegex_data$ " ' , get_output = True )
2024-04-21 16:05:06 +02:00
def nfqueue_exists ( ) :
2024-04-22 01:06:06 +02:00
import socket , fcntl , os , time
NETLINK_NETFILTER = 12
SOL_NETLINK = 270
NETLINK_EXT_ACK = 11
try :
nfsock = socket . socket ( socket . AF_NETLINK , socket . SOCK_RAW , NETLINK_NETFILTER )
fcntl . fcntl ( nfsock , fcntl . F_SETFL , os . O_RDONLY | os . O_NONBLOCK )
nfsock . setsockopt ( SOL_NETLINK , NETLINK_EXT_ACK , 1 )
except Exception as e :
return False
for rev in [ 3 , 2 , 1 , 0 ] :
timestamp = int ( time . time ( ) ) . to_bytes ( 4 , byteorder = ' big ' )
rev = rev . to_bytes ( 4 , byteorder = ' big ' )
#Prepared payload to check if the nfqueue module is loaded (from iptables code "nft_compatible_revision")
payload = b " \x30 \x00 \x00 \x00 \x00 \x0b \x05 \x00 " + timestamp + b " \x00 \x00 \x00 \x00 \x02 \x00 \x00 \x00 \x0c \x00 \x01 \x00 \x4e \x46 \x51 \x55 \x45 \x55 \x45 \x00 \x08 \x00 \x02 \x00 " + rev + b " \x08 \x00 \x03 \x00 \x00 \x00 \x00 \x01 "
nfsock . send ( payload )
data = nfsock . recv ( 1024 )
is_error = data [ 4 ] == 2
if not is_error : return True # The module exists and we have permission to use it
error_code = int . from_bytes ( data [ 16 : 16 + 4 ] , signed = True , byteorder = ' little ' )
if error_code == - 1 : return True # EPERM (the user is not root, but the module exists)
if error_code == - 2 : pass # ENOENT (the module does not exist)
else :
puts ( " Error while trying to check if the nfqueue module is loaded, this check will be skipped! " , color = colors . yellow )
return True
return False
2024-04-21 16:05:06 +02:00
def delete_volume ( ) :
return dockercmd ( " volume rm firegex_firegex_data " )
2023-04-12 23:53:43 +02:00
2024-04-21 16:05:06 +02:00
def main ( ) :
if not check_if_exists ( " docker " ) :
puts ( " Docker not found! please install docker and docker compose! " , color = colors . red )
2023-04-13 17:56:35 +02:00
exit ( )
2024-04-21 16:05:06 +02:00
elif not check_if_exists ( " docker-compose " ) and not check_if_exists ( " docker compose " ) :
print ( check_if_exists ( " docker-compose " ) , check_if_exists ( " docker compose " ) )
puts ( " Docker compose not found! please install docker compose! " , color = colors . red )
exit ( )
if not check_if_exists ( " docker ps " ) :
puts ( " Cannot use docker, the user hasn ' t the permission or docker isn ' t running " , color = colors . red )
exit ( )
if not is_linux ( ) :
sep ( )
puts ( " --- WARNING --- " , color = colors . yellow )
puts ( " You are not in a linux machine, the firewall will not work in this machine. " , color = colors . red )
sep ( )
elif not nfqueue_exists ( ) :
sep ( )
puts ( " --- WARNING --- " , color = colors . yellow )
puts ( " The nfqueue kernel module seems not loaded, some features of firegex may not work. " , color = colors . red )
sep ( )
2024-04-22 01:06:06 +02:00
2024-04-21 18:15:23 +02:00
2024-04-21 16:05:06 +02:00
if args . command :
match args . command :
case " start " :
if check_already_running ( ) :
puts ( " Firegex is already running! use --help to see options useful to manage firegex execution " , color = colors . yellow )
else :
puts ( f " Firegex " , color = colors . yellow , end = " " )
puts ( " will start on port " , end = " " )
puts ( f " { args . port } " , color = colors . cyan )
write_compose ( skip_password = False )
if not args . build :
puts ( " Downloading docker image from github packages ' docker pull ghcr.io/pwnzer0tt1/firegex ' " , color = colors . green )
dockercmd ( " pull ghcr.io/pwnzer0tt1/firegex " )
puts ( " Running ' docker compose up -d --build ' \n " , color = colors . green )
2024-07-04 14:31:41 +02:00
composecmd ( " up -d --build " , g . composefile )
2024-04-21 16:05:06 +02:00
case " compose " :
2023-04-13 17:56:35 +02:00
write_compose ( )
2024-04-21 16:05:06 +02:00
compose_cmd = " " . join ( args . compose_args )
puts ( f " Running ' docker compose { compose_cmd } ' \n " , color = colors . green )
2024-07-04 14:31:41 +02:00
composecmd ( compose_cmd , g . composefile )
2024-04-21 16:05:06 +02:00
case " restart " :
if check_already_running ( ) :
write_compose ( )
puts ( " Running ' docker compose restart ' \n " , color = colors . green )
2024-07-04 14:31:41 +02:00
composecmd ( " restart " , g . composefile )
2024-04-21 16:05:06 +02:00
else :
puts ( " Firegex is not running! " , color = colors . red , is_bold = True , flush = True )
case " stop " :
if check_already_running ( ) :
write_compose ( )
puts ( " Running ' docker compose down ' \n " , color = colors . green )
2024-07-04 14:31:41 +02:00
composecmd ( " down " , g . composefile )
2024-04-21 16:05:06 +02:00
else :
puts ( " Firegex is not running! " , color = colors . red , is_bold = True , flush = True )
write_compose ( )
2024-04-21 17:17:43 +02:00
if args . clear :
2024-04-21 16:05:06 +02:00
if volume_exists ( ) :
delete_volume ( )
else :
puts ( " Firegex volume not found! " , color = colors . red )
2023-04-13 17:56:35 +02:00
2024-04-21 16:05:06 +02:00
if " logs " in args and args . logs :
composecmd ( " logs -f " )
2023-04-13 17:56:35 +02:00
2023-04-12 23:53:43 +02:00
2024-04-21 16:05:06 +02:00
if __name__ == " __main__ " :
try :
2023-04-13 17:56:35 +02:00
try :
2024-04-21 16:05:06 +02:00
main ( )
2023-04-13 17:56:35 +02:00
finally :
2024-07-04 14:31:41 +02:00
if os . path . isfile ( g . composefile ) :
os . remove ( g . composefile )
2023-04-13 17:56:35 +02:00
except KeyboardInterrupt :
print ( )