This commit is contained in:
n.diaz
2024-11-25 19:12:51 +01:00
parent f917303ec8
commit 74638343ae

47
waf3.py
View File

@@ -12,36 +12,45 @@ import yaml
from peewee import fn from peewee import fn
from rich import print from rich import print
base_path = Path('/var/opt/waf') base_path = Path('/var/opt/waf')
conf_file = base_path / 'config.yml' conf_file = base_path / 'config.yml'
db_path = base_path / 'waf.db'
if conf_file.exists(): if conf_file.exists():
conf = yaml.safe_load(conf_file.text()) conf = yaml.safe_load(conf_file.text())
else: else:
conf = { conf = {
'vroots': "/srv", 'vroots': "/srv",
'whitelist_ips': [],
'date_range': 'last_hour',
'purge_older_than': 'three_days',
'db_filename': 'waf.db',
} }
conf_file.touch() conf_file.touch()
yaml.dump(conf, conf_file.open('w')) yaml.dump(conf, conf_file.open('w'))
# with open('names.yaml', 'w') as file:
# db_path = base_path / 'waf.db'
now = arrow.utcnow() now = arrow.utcnow()
older_than = now.shift(days=-3).floor('day') older_than = now.shift(days=-3).floor('day')
last_hour = now.shift(hours=-1).floor('hour') last_hour = now.shift(hours=-1).floor('hour')
last_thirty_min = now.shift(minutes=-30) last_thirty_min = now.shift(minutes=-30)
# last_period = last_thirty_min
last_period = last_hour # Configuring
db_path = base_path / globals()[conf['db_filename']]
date_range = globals()[conf['date_range']]
vroots = Path(conf['vroots']) vroots = Path(conf['vroots'])
logs = vroots.glob('*/logs/*access*.log') logs = vroots.glob('*/logs/*access*.log')
whitelist_ips = conf['whitelist_ips'] whitelist_ips = conf['whitelist_ips']
# Database
db = SqliteExtDatabase(db_path, pragmas={'journal_mode': 'wal'}) db = SqliteExtDatabase(db_path, pragmas={'journal_mode': 'wal'})
class Attack(peewee.Model): class Attack(peewee.Model):
ip = peewee.CharField(unique=True) ip = peewee.CharField(unique=True)
host = peewee.CharField(index = True) host = peewee.CharField(index = True)
date = peewee.DateTimeField(default=now.datetime) date = peewee.DateTimeField(default=now.datetime)
# date = peewee.DateTimeField(default=datetime.datetime.now)
count = peewee.BigIntegerField() count = peewee.BigIntegerField()
class Meta: class Meta:
@@ -49,17 +58,16 @@ class Attack(peewee.Model):
Attack.create_table(True) Attack.create_table(True)
# CLI App
@click.group() @click.group()
def cli(): def cli():
pass pass
async def nginx_reload(): async def nginx_reload():
returned_value = subprocess.call('/usr/bin/systemctl reload nginx', shell=True) returned_value = subprocess.call('/usr/bin/systemctl reload nginx', shell=True)
if returned_value == 0: if returned_value == 0:
click.echo(click.style('Nginx reloaded', fg="blue")) click.echo(click.style('Nginx reloaded', fg="blue"))
async def get_denied(): async def get_denied():
denied = [] denied = []
for rule in ufw.get_rules().values(): for rule in ufw.get_rules().values():
@@ -67,11 +75,9 @@ async def get_denied():
denied.append(rule.split(' ')[-1]) denied.append(rule.split(' ')[-1])
return denied return denied
async def deny(ip): async def deny(ip):
ufw.add('deny from ' + ip + ' to any', number=1) ufw.add('deny from ' + ip + ' to any', number=1)
def undeny(ip): def undeny(ip):
# Not async to avoid deleting wrong ufw rule. # Not async to avoid deleting wrong ufw rule.
for key, rule in ufw.get_rules().items(): for key, rule in ufw.get_rules().items():
@@ -82,10 +88,9 @@ def undeny(ip):
# if attack: # if attack:
# attack.delete_instance() # attack.delete_instance()
async def check(ip, host, date_position): async def check(ip, host, date_position):
date = arrow.get(date_position,'DD/MMM/YYYY:HH:mm:ss') date = arrow.get(date_position,'DD/MMM/YYYY:HH:mm:ss')
if date > last_period: if date > date_range:
find = Attack.get_or_none(Attack.ip == ip) find = Attack.get_or_none(Attack.ip == ip)
if find: if find:
find.count = find.count + 1 find.count = find.count + 1
@@ -94,7 +99,6 @@ async def check(ip, host, date_position):
data = {'ip': ip, 'date':date.datetime, 'host': host, 'count':1} data = {'ip': ip, 'date':date.datetime, 'host': host, 'count':1}
Attack.create(**data) Attack.create(**data)
checklist = [ checklist = [
{ {
'where': 'url', 'where': 'url',
@@ -111,11 +115,6 @@ checklist = [
'in': "\\x00", 'in': "\\x00",
'store': 'suspects', 'store': 'suspects',
}, },
{
'where': 'url',
'in': 'xmlrpc',
'store': 'suspects',
},
{ {
'method': 'post', 'method': 'post',
'where': 'url', 'where': 'url',
@@ -145,7 +144,6 @@ async def scan(log):
host = log.parent.parent.basename() host = log.parent.parent.basename()
if ip not in whitelist_ips: if ip not in whitelist_ips:
for rule in checklist: for rule in checklist:
# print(rule)
where = url where = url
if rule['where'] == 'url': if rule['where'] == 'url':
where = url where = url
@@ -154,24 +152,15 @@ async def scan(log):
store = suspects store = suspects
if 'in' in rule and rule['in'] in where: if 'in' in rule and rule['in'] in where:
store.append(check(ip, host, date_position)) store.append(check(ip, host, date_position))
#if 'xmlrpc' not in where:
# print(host)
break break
elif 'startswith' in rule and url.startswith(rule['startswith']) and 'notin' in rule and rule['notin'] not in url: 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)) store.append(check(ip, host, date_position))
# print(2)
break break
elif 'startswith' in rule and url.startswith(rule['startswith']): elif 'startswith' in rule and url.startswith(rule['startswith']):
store.append(check(ip, host, date_position)) store.append(check(ip, host, date_position))
# print(3)
break 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']: # elif 'wp-admin' in url and status not in ['200','302','499']:
# suspects.append(check(ip, host, date_position)) # store.append(check(ip, host, date_position))
# def is_suspicious_login(item): # def is_suspicious_login(item):
# return len(item[1]) > 18 # return len(item[1]) > 18