unblock, info commands. no login supicious
This commit is contained in:
112
waf3.py
112
waf3.py
@ -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()
|
||||
|
||||
Reference in New Issue
Block a user