Mini Shell
#!/opt/imh-python/bin/python3
"""wrapper script for blocking/unblocking IPs
on shared via ipset
Also ensures that the IPs are safe to block by
importing the imh-whcheck module from imh-fail2ban"""
import sys
import subprocess
import syslog
from socket import AddressFamily # pylint:disable=no-name-in-module
from argparse import ArgumentParser
import psutil
from netaddr import IPNetwork
def parse_cli():
"""
Parse CLI arguments
"""
parser = ArgumentParser(description="ipset wrapper for IMH shared firewall")
mgroup = parser.add_mutually_exclusive_group(required=True)
mgroup.add_argument(
'--block',
'-d',
action='store',
nargs=2,
metavar=("IP/CIDR", "REASON"),
help="IP Address or network to block + reason/comment",
)
mgroup.add_argument(
'--unblock',
'-u',
action='store',
metavar="IP/CIDR",
help="IP Address or network to unblock",
)
return parser.parse_args()
def check_ignore(ip):
"""
Check if @ip is safe block, or should be ignored
This uses the imh-whcheck module from imh-fail2ban
returns True if we should ignore, otherwise False
"""
# pylint: disable=import-error,import-outside-toplevel
try:
sys.path.insert(0, '/opt/sharedrads/python')
import imh_whcheck
except Exception as exc:
print("ERROR: Failed to load whcheck source:", exc)
return False
return imh_whcheck.check_ip(ip)
def check_local(ip):
"""
Check if @ip is local
"""
local_ips = [
IPNetwork(x[0].address)
for x in list(psutil.net_if_addrs().values())
if x[0].family is AddressFamily.AF_INET
]
for lip in local_ips:
if ip == lip:
return True
return False
def write_syslog(msg):
"""
Track actions via syslog and logging servers
"""
syslog.openlog('blockip')
syslog.syslog(msg)
syslog.closelog()
def ipset(cmd: str, ip, setname='blacklist', comment=''):
"""
Run ipset with specified @cmd for @ip on @setname
"""
try:
ret = subprocess.run(
['ipset', cmd, setname, str(ip)],
encoding='utf-8',
stderr=subprocess.PIPE,
stdout=subprocess.DEVNULL,
check=False,
)
except Exception as e:
print("ERROR: Failed to execute ipset: %s" % (str(e)))
return
if ret.returncode == 0:
write_syslog(f"{setname} {cmd} {ip} - {comment}")
print(f"SUCCESS: {setname} {cmd} {ip} ({comment})")
else:
print(f"FAILED: {setname} {cmd} {ip}: {ret.stderr}")
def _main():
"""Entry point"""
args = parse_cli()
ip_list = args.block if args.block else [args.unblock]
try:
tip = IPNetwork(ip_list[0])
except Exception as e:
print("ERROR: %s" % (str(e)))
sys.exit(1)
if tip.size > 1024:
print("ERROR: Using networks larger than /22 is not allowed!")
sys.exit(2)
elif check_local(tip):
print(
"ERROR: Unable to block local IP %s (what are you even doing?)"
% (tip)
)
sys.exit(2)
elif check_ignore(tip):
print("ERROR: Unable to block IP %s in whitelist" % (tip))
sys.exit(2)
elif args.block:
ipset('add', tip, comment=ip_list[1])
elif args.unblock:
ipset('del', tip)
if __name__ == '__main__':
_main()
Zerion Mini Shell 1.0