unblock, info commands. no login supicious

This commit is contained in:
root
2023-04-09 21:19:36 +02:00
parent d985dff839
commit 369a1a90cd

112
waf3.py
View File

@ -24,6 +24,7 @@ else:
# with open('names.yaml', 'w') as file:
now = arrow.utcnow()
older_than = now.shift(days=-3).floor('day')
last_hour = now.shift(hours=-1).floor('hour')
last_thirty_min = now.shift(minutes=-30)
# last_period = last_thirty_min
@ -49,10 +50,11 @@ Attack.create_table(True)
def cli():
pass
async def nginx_reload():
returned_value = subprocess.call('/usr/bin/systemctl reload nginx', shell=True)
if returned_value == 0:
print('Nginx reloaded')
click.echo(click.style('Nginx reloaded', fg="blue"))
async def get_denied():
denied = []
@ -61,9 +63,22 @@ async def get_denied():
denied.append(rule.split(' ')[-1])
return denied
async def deny(ip):
ufw.add('deny from ' + ip + ' to any', number=1)
def undeny(ip):
# Not async to avoid deleting wrong ufw rule.
for key, rule in ufw.get_rules().items():
if rule == "deny from " + ip:
ufw.delete(key)
# attack = Attack.get_or_none(Attack.ip == ip)
# if attack:
# attack.delete_instance()
async def check(ip, host, date_position):
date = arrow.get(date_position,'DD/MMM/YYYY:HH:mm:ss')
if date > last_period:
@ -75,9 +90,10 @@ async def check(ip, host, date_position):
data = {'ip': ip, 'date':date.datetime, 'host': host, 'count':1}
Attack.create(**data)
async def scan(log):
suspects = []
suspects_login = {}
# suspects_login = {}
suspects_404 = {}
for line in log.lines():
splitted = line.split()
@ -92,25 +108,28 @@ async def scan(log):
suspects.append(check(ip, host, date_position))
elif 'xmlrpc.php' in url:
suspects.append(check(ip, host, date_position))
elif 'login.php' in url and method == 'POST':
if ip in suspects_login:
suspects_login[ip].append( (ip, host, date_position) )
else:
suspects_login[ip] = [(ip, host, date_position),]
# elif 'login.php' in url and method == 'POST':
# if ip in suspects_login:
# suspects_login[ip].append( (ip, host, date_position) )
# else:
# suspects_login[ip] = [(ip, host, date_position),]
elif 'wp-admin' in url and status not in ['200','302','499']:
suspects.append(check(ip, host, date_position))
def is_suspicious_login(item):
return len(item[1]) > 18
filtered = dict(filter(is_suspicious_login, suspects_login.items()))
for ip,suspect in filtered.items():
suspects.append(check(ip, suspect[-1][1], suspect[-1][2]))
# def is_suspicious_login(item):
# return len(item[1]) > 18
# filtered = dict(filter(is_suspicious_login, suspects_login.items()))
# for ip,suspect in filtered.items():
# suspects.append(check(ip, suspect[-1][1], suspect[-1][2]))
await asyncio.gather(*suspects)
async def block():
denied = await get_denied()
found = Attack.select().where(
(Attack.ip.not_in(denied)) &
(Attack.count > 7)
(Attack.count > 3)
)
if found.count() > 0:
click.echo(click.style('New IPs to block: {}'.format(found.count()), fg="yellow"))
@ -118,9 +137,19 @@ async def block():
for attack in found:
to_deny.append( deny(attack.ip) )
await asyncio.gather(*to_deny)
await nginx_reload()
else:
click.echo(click.style('No IPs to block', fg="blue"))
def report_attacks():
click.echo(
click.style(
f"Attacks in database: {Attack.select().count()}",
fg="cyan"
)
)
def report():
click.echo(
click.style(
@ -134,12 +163,7 @@ def report():
fg="cyan"
)
)
click.echo(
click.style(
f"Attacks in database: {Attack.select().count()}",
fg="cyan"
)
)
report_attacks()
for ip in whitelist_ips:
click.echo(
click.style(
@ -148,6 +172,7 @@ def report():
)
)
async def start():
report()
scans = []
@ -155,17 +180,65 @@ async def start():
scans.append(scan(log))
await asyncio.gather(*scans)
await block()
report_attacks()
#print("Updated number of attacks in database: {}".format( Attack.select().count() ) )
async def info(ip, unblock=False):
denied = await get_denied()
find = Attack.get_or_none(Attack.ip == ip)
if find:
click.echo(click.style(f'IP {find.ip} found with {find.count} attacks', fg="yellow", bold=True))
# print(find)
if unblock is True:
find.delete_instance()
click.echo(click.style(f'IP {ip} removed from attacks', fg="green", bold=True))
else:
click.echo(click.style(f'IP {ip} no attacks found', fg="blue"))
if ip in denied:
if unblock is False:
click.echo(click.style(f'IP {ip} is BLOCKED', fg="yellow", bold=True))
else:
undeny(ip)
click.echo(click.style(f'IP {ip} UNBLOCKED', fg="green", bold=True))
async def purge():
print(older_than)
found = Attack.select(Attack.ip).where(
(Attack.date < older_than.datetime ) )
for attack in found:
undeny(attack.ip)
attack.delete_instance()
@cli.command('info')
@click.argument('ip')
def waf_info(ip):
click.echo('Find blocked IP')
asyncio.run(info(ip))
@cli.command('unblock')
@click.argument('ip')
def waf_unblock(ip):
click.echo('Unblock blocked IP')
asyncio.run(info(ip,unblock=True))
@cli.command('purge')
def waf_purge():
click.echo('Clean all blocked IPs')
asyncio.run(purge())
@cli.command('report')
def waf_report():
click.echo(click.style('Report WAF attacks', fg="blue", bold=True))
report()
@cli.command('scan')
def waf_scan():
before = arrow.utcnow()
@ -175,5 +248,6 @@ def waf_scan():
after = arrow.utcnow()
click.echo(click.style(f'Finished in {after - before}', fg="blue"))
if __name__ == '__main__':
cli()