This commit is contained in:
root
2023-04-25 11:59:50 +02:00
parent 369a1a90cd
commit f917303ec8

115
waf3.py
View File

@ -9,6 +9,9 @@ from path import Path
import pyufw as ufw
from playhouse.sqlite_ext import SqliteExtDatabase
import yaml
from peewee import fn
from rich import print
base_path = Path('/var/opt/waf')
conf_file = base_path / 'config.yml'
@ -56,6 +59,7 @@ async def nginx_reload():
if returned_value == 0:
click.echo(click.style('Nginx reloaded', fg="blue"))
async def get_denied():
denied = []
for rule in ufw.get_rules().values():
@ -91,6 +95,42 @@ async def check(ip, host, date_position):
Attack.create(**data)
checklist = [
{
'where': 'url',
'in': 'xmlrpc',
'store': 'suspects',
},
{
'where': 'url',
'in': "shell",
'store': 'suspects',
},
{
'where': 'url',
'in': "\\x00",
'store': 'suspects',
},
{
'where': 'url',
'in': 'xmlrpc',
'store': 'suspects',
},
{
'method': 'post',
'where': 'url',
'in': 'wp-login',
'store': 'suspects',
},
{
'where': 'url',
'startswith': '/.',
'notin': '.well_known',
'store': 'suspects',
},
]
async def scan(log):
suspects = []
# suspects_login = {}
@ -104,17 +144,34 @@ async def scan(log):
date_position = splitted[3][1:]
host = log.parent.parent.basename()
if ip not in whitelist_ips:
if url.startswith('/.') and '.well-known' not in url:
suspects.append(check(ip, host, date_position))
elif 'xmlrpc.php' in url:
suspects.append(check(ip, host, date_position))
for rule in checklist:
# print(rule)
where = url
if rule['where'] == 'url':
where = url
store = suspects
if rule['store'] == 'suspects':
store = suspects
if 'in' in rule and rule['in'] in where:
store.append(check(ip, host, date_position))
#if 'xmlrpc' not in where:
# print(host)
break
elif 'startswith' in rule and url.startswith(rule['startswith']) and 'notin' in rule and rule['notin'] not in url:
store.append(check(ip, host, date_position))
# print(2)
break
elif 'startswith' in rule and url.startswith(rule['startswith']):
store.append(check(ip, host, date_position))
# print(3)
break
# 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))
# 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
@ -122,14 +179,16 @@ async def scan(log):
# 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 > 3)
(Attack.count > 15)
)
if found.count() > 0:
click.echo(click.style('New IPs to block: {}'.format(found.count()), fg="yellow"))
@ -149,6 +208,25 @@ def report_attacks():
fg="cyan"
)
)
hosts = {}
for a in Attack.select():
# print(a.host)
if a.host in hosts:
hosts[a.host] = hosts[a.host] + 1
else:
hosts[a.host] = 1
sorted_hosts = dict(sorted(hosts.items(), key=lambda x:x[1]))
# print(sorted_footballers_by_goals)
# query = (Attack
# .select( Attack.id, Attack.host, fn.SUM(Attack.host).alias('sum_host') )
# .group_by(Attack.host)
# .order_by( fn.SUM(Attack.host).alias('sum_host') ))
for h, v in sorted_hosts.items():
print(h, v)
def report():
click.echo(
@ -188,12 +266,10 @@ 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)
click.echo(click.style(f'IP {find.ip} found with {find.count} attacks at {find.host}', fg="yellow", bold=True))
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"))
@ -204,7 +280,8 @@ async def info(ip, unblock=False):
undeny(ip)
click.echo(click.style(f'IP {ip} UNBLOCKED', fg="green", bold=True))
async def purge():
def purge():
print(older_than)
found = Attack.select(Attack.ip).where(
(Attack.date < older_than.datetime ) )
@ -213,6 +290,14 @@ async def purge():
attack.delete_instance()
def empty():
found = Attack.select()
for attack in found:
# print(attack.ip)
undeny(attack.ip)
attack.delete_instance()
@cli.command('info')
@click.argument('ip')
def waf_info(ip):
@ -229,8 +314,14 @@ def waf_unblock(ip):
@cli.command('purge')
def waf_purge():
click.echo('Clean old blocked IPs')
purge()
@cli.command('empty')
def waf_empty():
click.echo('Clean all blocked IPs')
asyncio.run(purge())
empty()
@cli.command('report')