go up
This commit is contained in:
115
waf3.py
115
waf3.py
@ -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')
|
||||
|
Reference in New Issue
Block a user