wafaHell 1.0.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wafaHell/app.py +3 -3
- wafaHell/logger.py +6 -5
- wafaHell/middleware.py +276 -159
- wafaHell/model.py +11 -0
- wafaHell/panel.py +254 -40
- wafaHell/utils.py +303 -199
- {wafahell-1.0.0.dist-info → wafahell-1.1.0.dist-info}/METADATA +1 -1
- wafahell-1.1.0.dist-info/RECORD +15 -0
- wafahell-1.0.0.dist-info/RECORD +0 -15
- {wafahell-1.0.0.dist-info → wafahell-1.1.0.dist-info}/WHEEL +0 -0
- {wafahell-1.0.0.dist-info → wafahell-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {wafahell-1.0.0.dist-info → wafahell-1.1.0.dist-info}/top_level.txt +0 -0
wafaHell/utils.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
from .model import WafLog, Blocked, Whitelist, AdminUser, get_session
|
|
2
|
+
from .globals import waf_cache
|
|
1
3
|
from datetime import datetime, timedelta, timezone
|
|
2
4
|
import secrets
|
|
5
|
+
import socket
|
|
3
6
|
import string
|
|
4
7
|
import time
|
|
8
|
+
import tomllib
|
|
5
9
|
from werkzeug.security import generate_password_hash
|
|
6
10
|
from sqlalchemy.orm import Session
|
|
7
11
|
from sqlalchemy import text, func, case
|
|
8
|
-
from model import WafLog, Blocked
|
|
9
|
-
from model import AdminUser
|
|
10
12
|
from functools import wraps
|
|
11
13
|
from flask import session, redirect, url_for, request
|
|
12
|
-
from model import get_session
|
|
13
|
-
from globals import waf_cache
|
|
14
14
|
import geoip2.database
|
|
15
15
|
import os
|
|
16
16
|
|
|
@@ -43,170 +43,197 @@ class Admin:
|
|
|
43
43
|
def admin(fn):
|
|
44
44
|
@wraps(fn)
|
|
45
45
|
def wrapper(*args, **kwargs):
|
|
46
|
-
print(f"DEBUG: Session logged_in status: {session.get('logged_in')}") # Adicione isso
|
|
47
46
|
if not session.get("logged_in"):
|
|
48
|
-
print("DEBUG: Redirecting to login...")
|
|
49
47
|
return redirect(url_for("login", next=request.path))
|
|
50
48
|
return fn(*args, **kwargs)
|
|
51
49
|
return wrapper
|
|
52
50
|
|
|
53
51
|
class Dashboard:
|
|
54
52
|
def __init__(self):
|
|
55
|
-
self.db_session = get_session()
|
|
56
53
|
self.geo_db_path = os.path.join(os.path.dirname(__file__), 'GeoLite2-Country.mmdb')
|
|
57
54
|
|
|
58
55
|
def dashboard_setup(self):
|
|
59
56
|
json = {}
|
|
57
|
+
|
|
58
|
+
def get_waf_version():
|
|
59
|
+
base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
60
|
+
toml_path = os.path.join(base_path, "pyproject.toml")
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
with open(toml_path, "rb") as f:
|
|
64
|
+
data = tomllib.load(f)
|
|
65
|
+
return data["project"]["version"]
|
|
66
|
+
except FileNotFoundError:
|
|
67
|
+
return "v.0.0.0"
|
|
68
|
+
|
|
60
69
|
def get_server_info():
|
|
61
70
|
server_time = datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z')
|
|
62
|
-
node_id =
|
|
71
|
+
node_id = socket.gethostname()
|
|
63
72
|
avg_latency = waf_cache.get('latency_avg', default=0.0)
|
|
64
73
|
system_status = "critical" if avg_latency > 500 else "degraded" if avg_latency > 200 else "healthy"
|
|
65
74
|
return {
|
|
66
75
|
"server_time": server_time,
|
|
67
76
|
"node_id": node_id,
|
|
68
77
|
"average_latency_ms": round(float(avg_latency), 2),
|
|
69
|
-
"system_status": system_status
|
|
78
|
+
"system_status": system_status,
|
|
79
|
+
"version": get_waf_version()
|
|
70
80
|
}
|
|
71
81
|
|
|
72
82
|
def get_kpis():
|
|
73
83
|
now = datetime.now(timezone.utc)
|
|
74
84
|
last_24h = now - timedelta(hours=24)
|
|
75
85
|
prev_24h = now - timedelta(hours=48)
|
|
76
|
-
|
|
77
|
-
# --- TOTAIS DE HOJE ---
|
|
78
|
-
total_today = self.db_session.query(func.count(WafLog.id)).filter(WafLog.timestamp >= last_24h).scalar() or 0
|
|
79
|
-
blocked_today = self.db_session.query(func.count(WafLog.id)).filter(
|
|
80
|
-
WafLog.timestamp >= last_24h,
|
|
81
|
-
WafLog.attack_type != 'INFO'
|
|
82
|
-
).scalar() or 0
|
|
83
|
-
|
|
84
|
-
# --- TOTAIS DE ONTEM (Para Tendência) ---
|
|
85
|
-
total_yesterday = self.db_session.query(func.count(WafLog.id)).filter(
|
|
86
|
-
WafLog.timestamp >= prev_24h,
|
|
87
|
-
WafLog.timestamp < last_24h
|
|
88
|
-
).scalar() or 0
|
|
89
|
-
|
|
90
|
-
blocked_yesterday = self.db_session.query(func.count(WafLog.id)).filter(
|
|
91
|
-
WafLog.timestamp >= prev_24h,
|
|
92
|
-
WafLog.timestamp < last_24h,
|
|
93
|
-
WafLog.attack_type != 'INFO'
|
|
94
|
-
).scalar() or 0
|
|
95
|
-
|
|
96
|
-
# --- CÁLCULO DE TENDÊNCIA (%) ---
|
|
97
|
-
def calc_trend(current, previous):
|
|
98
|
-
if previous == 0:
|
|
99
|
-
return 100.0 if current > 0 else 0.0
|
|
100
|
-
return round(((current - previous) / previous * 100), 1)
|
|
101
|
-
|
|
102
|
-
trend_total = calc_trend(total_today, total_yesterday)
|
|
103
|
-
trend_attacks = calc_trend(blocked_today, blocked_yesterday)
|
|
104
|
-
|
|
105
|
-
# --- INFOS COMPLEMENTARES ---
|
|
106
|
-
from globals import waf_cache
|
|
107
|
-
# Latência e RPS vindos do Cache Global
|
|
108
|
-
last_second_timestamp = int(time.time()) - 1
|
|
109
|
-
rps_key = f"rps_{last_second_timestamp}"
|
|
110
|
-
|
|
111
|
-
# 2. Lê o valor real do cache para esse segundo específico
|
|
112
|
-
current_rps = waf_cache.get(rps_key, default=0)
|
|
113
|
-
|
|
114
|
-
# 3. Lógica do Pico (Peak)
|
|
115
|
-
# Aqui continuamos usando uma chave fixa 'rps_peak_hour' para guardar o recorde
|
|
116
|
-
peak_rps = waf_cache.get('rps_peak_hour', default=0)
|
|
117
|
-
|
|
118
|
-
if current_rps > peak_rps:
|
|
119
|
-
peak_rps = current_rps
|
|
120
|
-
# Atualiza o recorde no cache por 1 hora
|
|
121
|
-
waf_cache.set('rps_peak_hour', peak_rps, expire=3600)
|
|
86
|
+
session = get_session()
|
|
122
87
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
88
|
+
try:
|
|
89
|
+
# --- TOTAIS DE HOJE ---
|
|
90
|
+
total_today = session.query(func.count(WafLog.id)).filter(WafLog.timestamp >= last_24h).scalar() or 0
|
|
91
|
+
blocked_today = session.query(func.count(WafLog.id)).filter(
|
|
92
|
+
WafLog.timestamp >= last_24h,
|
|
93
|
+
WafLog.attack_type != 'INFO'
|
|
94
|
+
).scalar() or 0
|
|
95
|
+
|
|
96
|
+
# --- TOTAIS DE ONTEM (Para Tendência) ---
|
|
97
|
+
total_yesterday = session.query(func.count(WafLog.id)).filter(
|
|
98
|
+
WafLog.timestamp >= prev_24h,
|
|
99
|
+
WafLog.timestamp < last_24h
|
|
100
|
+
).scalar() or 0
|
|
101
|
+
|
|
102
|
+
blocked_yesterday = session.query(func.count(WafLog.id)).filter(
|
|
103
|
+
WafLog.timestamp >= prev_24h,
|
|
104
|
+
WafLog.timestamp < last_24h,
|
|
105
|
+
WafLog.attack_type != 'INFO'
|
|
106
|
+
).scalar() or 0
|
|
107
|
+
|
|
108
|
+
# --- CÁLCULO DE TENDÊNCIA (%) ---
|
|
109
|
+
def calc_trend(current, previous):
|
|
110
|
+
if previous == 0:
|
|
111
|
+
return 100.0 if current > 0 else 0.0
|
|
112
|
+
return round(((current - previous) / previous * 100), 1)
|
|
113
|
+
|
|
114
|
+
trend_total = calc_trend(total_today, total_yesterday)
|
|
115
|
+
trend_attacks = calc_trend(blocked_today, blocked_yesterday)
|
|
116
|
+
|
|
117
|
+
# --- INFOS COMPLEMENTARES ---
|
|
118
|
+
from globals import waf_cache
|
|
119
|
+
# Latência e RPS vindos do Cache Global
|
|
120
|
+
last_second_timestamp = int(time.time()) - 1
|
|
121
|
+
rps_key = f"rps_{last_second_timestamp}"
|
|
122
|
+
|
|
123
|
+
# 2. Lê o valor real do cache para esse segundo específico
|
|
124
|
+
current_rps = waf_cache.get(rps_key, default=0)
|
|
125
|
+
|
|
126
|
+
# 3. Lógica do Pico (Peak)
|
|
127
|
+
# Aqui continuamos usando uma chave fixa 'rps_peak_hour' para guardar o recorde
|
|
128
|
+
peak_rps = waf_cache.get('rps_peak_hour', default=0)
|
|
129
|
+
|
|
130
|
+
if current_rps > peak_rps:
|
|
131
|
+
peak_rps = current_rps
|
|
132
|
+
# Atualiza o recorde no cache por 1 hora
|
|
133
|
+
waf_cache.set('rps_peak_hour', peak_rps, expire=3600)
|
|
134
|
+
|
|
135
|
+
# Dados da Blacklist
|
|
136
|
+
total_blacklist = session.query(func.count(Blocked.id)).scalar() or 0
|
|
137
|
+
# Como blocked_at é String formatada no seu modelo, comparamos com o horário
|
|
138
|
+
added_today = session.query(func.count(Blocked.id)).filter(
|
|
139
|
+
Blocked.blocked_until >= now # Um IP "adicionado hoje" é tecnicamente um ainda bloqueado
|
|
140
|
+
).scalar() or 0
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
"total_requests_24h": {
|
|
144
|
+
"value": total_today,
|
|
145
|
+
"trend_percent": trend_total
|
|
146
|
+
},
|
|
147
|
+
"attacks_mitigated_24h": {
|
|
148
|
+
"value": blocked_today,
|
|
149
|
+
"trend_percent": trend_attacks # Adicionado aqui
|
|
150
|
+
},
|
|
151
|
+
"throughput": {
|
|
152
|
+
"current_req_per_sec": current_rps,
|
|
153
|
+
"peak_last_hour": peak_rps
|
|
154
|
+
},
|
|
155
|
+
"blacklist_count": {
|
|
156
|
+
"total_ips": total_blacklist,
|
|
157
|
+
"added_today": added_today
|
|
158
|
+
}
|
|
146
159
|
}
|
|
147
|
-
|
|
148
|
-
|
|
160
|
+
|
|
161
|
+
except Exception as e:
|
|
162
|
+
print(f"Erro no Dashboard: {e}")
|
|
163
|
+
finally:
|
|
164
|
+
session.close()
|
|
165
|
+
|
|
149
166
|
def get_traffic_chart():
|
|
150
167
|
now = datetime.now(timezone.utc)
|
|
151
168
|
start_time = now - timedelta(minutes=40)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
session = get_session()
|
|
170
|
+
try:
|
|
171
|
+
# Query para agrupar por minuto
|
|
172
|
+
# No SQLite usamos strftime, no Postgres/MySQL seria date_format ou similar
|
|
173
|
+
query = session.query(
|
|
174
|
+
func.strftime('%H:%M', WafLog.timestamp).label('minute'),
|
|
175
|
+
func.count(WafLog.id).label('total'),
|
|
176
|
+
func.sum(case({WafLog.attack_type == 'INFO': 1}, else_=0)).label('legit'),
|
|
177
|
+
func.sum(case({WafLog.attack_type != 'INFO': 1}, else_=0)).label('attacks')
|
|
178
|
+
).filter(WafLog.timestamp >= start_time)\
|
|
179
|
+
.group_by('minute')\
|
|
180
|
+
.order_by('minute').all()
|
|
181
|
+
|
|
182
|
+
labels = []
|
|
183
|
+
series_legit = []
|
|
184
|
+
series_attacks = []
|
|
185
|
+
|
|
186
|
+
# Preenche os arrays para o gráfico
|
|
187
|
+
for row in query:
|
|
188
|
+
labels.append(row.minute)
|
|
189
|
+
series_legit.append(row.legit or 0)
|
|
190
|
+
series_attacks.append(row.attacks or 0)
|
|
191
|
+
|
|
192
|
+
# Caso não existam dados nos últimos 40 min, retorna arrays vazios para não quebrar o front
|
|
193
|
+
return {
|
|
194
|
+
"labels": labels,
|
|
195
|
+
"series_legit": series_legit,
|
|
196
|
+
"series_attacks": series_attacks
|
|
197
|
+
}
|
|
198
|
+
except Exception as e:
|
|
199
|
+
print(f"Erro no Dashboard: {e}")
|
|
200
|
+
finally:
|
|
201
|
+
session.close()
|
|
180
202
|
|
|
181
203
|
def get_distribution_vectors():
|
|
182
204
|
now = datetime.now(timezone.utc)
|
|
183
205
|
last_24h = now - timedelta(hours=24)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
session = get_session()
|
|
207
|
+
try:
|
|
208
|
+
# 1. Buscamos a contagem agrupada por tipo de ataque
|
|
209
|
+
# Filtramos para não incluir tráfego legítimo (INFO)
|
|
210
|
+
query = session.query(
|
|
211
|
+
WafLog.attack_type,
|
|
212
|
+
func.count(WafLog.id).label('count')
|
|
213
|
+
).filter(
|
|
214
|
+
WafLog.timestamp >= last_24h,
|
|
215
|
+
WafLog.attack_type != 'INFO'
|
|
216
|
+
).group_by(WafLog.attack_type).all()
|
|
217
|
+
|
|
218
|
+
# 2. Calculamos o total de ataques para obter a porcentagem
|
|
219
|
+
total_attacks = sum(row.count for row in query)
|
|
220
|
+
|
|
221
|
+
distribution = []
|
|
222
|
+
|
|
223
|
+
for row in query:
|
|
224
|
+
percentage = round((row.count / total_attacks * 100), 1) if total_attacks > 0 else 0
|
|
225
|
+
distribution.append({
|
|
226
|
+
"label": row.attack_type,
|
|
227
|
+
"count": row.count,
|
|
228
|
+
"percentage": percentage
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
# Caso não haja ataques, retornamos uma lista vazia ou um placeholder
|
|
232
|
+
return distribution
|
|
233
|
+
except Exception as e:
|
|
234
|
+
print(f"Erro no Dashboard: {e}")
|
|
235
|
+
finally:
|
|
236
|
+
session.close()
|
|
210
237
|
|
|
211
238
|
def get_top_geo():
|
|
212
239
|
def resolve_ip(ip):
|
|
@@ -224,75 +251,85 @@ class Dashboard:
|
|
|
224
251
|
|
|
225
252
|
now = datetime.now(timezone.utc)
|
|
226
253
|
last_24h = now - timedelta(hours=24)
|
|
227
|
-
|
|
254
|
+
session = get_session()
|
|
228
255
|
# 1. Busca todos os ataques agrupados por IP
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
256
|
+
try:
|
|
257
|
+
query = session.query(
|
|
258
|
+
WafLog.ip,
|
|
259
|
+
func.count(WafLog.id).label('count')
|
|
260
|
+
).filter(
|
|
261
|
+
WafLog.timestamp >= last_24h,
|
|
262
|
+
WafLog.attack_type != 'INFO'
|
|
263
|
+
).group_by(WafLog.ip).all()
|
|
264
|
+
|
|
265
|
+
geo_stats = {}
|
|
266
|
+
total_attacks = 0
|
|
267
|
+
|
|
268
|
+
# 2. Processa cada IP real usando o GeoIP2
|
|
269
|
+
for row in query:
|
|
270
|
+
code, name = resolve_ip(row.ip)
|
|
271
|
+
|
|
272
|
+
if code not in geo_stats:
|
|
273
|
+
geo_stats[code] = {"name": name, "count": 0}
|
|
274
|
+
|
|
275
|
+
geo_stats[code]["count"] += row.count
|
|
276
|
+
total_attacks += row.count
|
|
277
|
+
|
|
278
|
+
# 3. Formata o Top 5
|
|
279
|
+
top_geo = []
|
|
280
|
+
sorted_geo = sorted(geo_stats.items(), key=lambda x: x[1]['count'], reverse=True)[:5]
|
|
281
|
+
|
|
282
|
+
for code, data in sorted_geo:
|
|
283
|
+
percentage = round((data["count"] / total_attacks * 100), 1) if total_attacks > 0 else 0
|
|
284
|
+
top_geo.append({
|
|
285
|
+
"country_code": code,
|
|
286
|
+
"country_name": data["name"],
|
|
287
|
+
"count": data["count"],
|
|
288
|
+
"percentage": percentage
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
return top_geo
|
|
292
|
+
except Exception as e:
|
|
293
|
+
print(f"Erro no Dashboard: {e}")
|
|
294
|
+
finally:
|
|
295
|
+
session.close()
|
|
264
296
|
|
|
265
297
|
def get_top_offenders():
|
|
266
298
|
now = datetime.now(timezone.utc)
|
|
267
299
|
last_24h = now - timedelta(hours=24)
|
|
268
|
-
|
|
300
|
+
session = get_session()
|
|
269
301
|
# 1. Agrupamos por IP e contamos os hits e os tipos de ataques diferentes
|
|
270
302
|
# Ignoramos tráfego INFO
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
303
|
+
try:
|
|
304
|
+
query = session.query(
|
|
305
|
+
WafLog.ip,
|
|
306
|
+
func.count(WafLog.id).label('hits_count'),
|
|
307
|
+
func.count(func.distinct(WafLog.attack_type)).label('unique_vectors')
|
|
308
|
+
).filter(
|
|
309
|
+
WafLog.timestamp >= last_24h,
|
|
310
|
+
WafLog.attack_type != 'INFO'
|
|
311
|
+
).group_by(WafLog.ip).order_by(text('hits_count DESC')).limit(5).all()
|
|
312
|
+
|
|
313
|
+
offenders = []
|
|
314
|
+
for row in query:
|
|
315
|
+
# 2. Lógica de Risk Score (0 a 100)
|
|
316
|
+
# Baseada em volume e diversidade de ataques
|
|
317
|
+
# Ex: Cada vetor único vale 20 pontos + 1 ponto para cada 50 hits (até o teto de 100)
|
|
318
|
+
vector_points = row.unique_vectors * 20
|
|
319
|
+
hit_points = row.hits_count // 50
|
|
320
|
+
risk_score = min(100, vector_points + hit_points)
|
|
321
|
+
|
|
322
|
+
offenders.append({
|
|
323
|
+
"ip": row.ip,
|
|
324
|
+
"risk_score": int(risk_score),
|
|
325
|
+
"hits_count": row.hits_count
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
return offenders
|
|
329
|
+
except Exception as e:
|
|
330
|
+
print(f"Erro no Dashboard: {e}")
|
|
331
|
+
finally:
|
|
332
|
+
session.close()
|
|
296
333
|
|
|
297
334
|
try:
|
|
298
335
|
json['meta'] = get_server_info()
|
|
@@ -307,6 +344,73 @@ class Dashboard:
|
|
|
307
344
|
except Exception as e:
|
|
308
345
|
print(f"Erro no Dashboard: {e}")
|
|
309
346
|
return {"error": str(e)}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
347
|
+
|
|
348
|
+
def seed_default_whitelist():
|
|
349
|
+
"""
|
|
350
|
+
Popula a Whitelist com IPs essenciais (Localhost, DNS públicos, etc)
|
|
351
|
+
Executar na inicialização do app.
|
|
352
|
+
"""
|
|
353
|
+
# Lista de IPs Padrão (Adicione aqui IPs unitários que confia)
|
|
354
|
+
DEFAULT_IPS = [
|
|
355
|
+
# --- Localhost / Loopback (Essencial) ---
|
|
356
|
+
"127.0.0.1",
|
|
357
|
+
"::1",
|
|
358
|
+
|
|
359
|
+
# Redes Privadas (As gigantes)
|
|
360
|
+
"10.0.0.0/8", # 16 milhões de IPs
|
|
361
|
+
"172.16.0.0/12", # 1 milhão de IPs
|
|
362
|
+
"192.168.0.0/16", # 65 mil IPs
|
|
363
|
+
|
|
364
|
+
# --- DNS Públicos (Google) ---
|
|
365
|
+
"8.8.8.8",
|
|
366
|
+
"8.8.4.4",
|
|
367
|
+
|
|
368
|
+
# --- DNS Públicos (Cloudflare) ---
|
|
369
|
+
"1.1.1.1",
|
|
370
|
+
"1.0.0.1",
|
|
371
|
+
|
|
372
|
+
# --- DNS Públicos (OpenDNS) ---
|
|
373
|
+
"208.67.222.222",
|
|
374
|
+
"208.67.220.220",
|
|
375
|
+
|
|
376
|
+
# --- DNS Públicos (Quad9) ---
|
|
377
|
+
"9.9.9.9",
|
|
378
|
+
"149.112.112.112"
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
session = get_session()
|
|
382
|
+
try:
|
|
383
|
+
# 1. Descobre o que já existe no banco para não duplicar
|
|
384
|
+
existing_query = session.query(Whitelist.ip).filter(Whitelist.ip.in_(DEFAULT_IPS)).all()
|
|
385
|
+
existing_ips = {row.ip for row in existing_query}
|
|
386
|
+
|
|
387
|
+
# 2. Filtra apenas os novos
|
|
388
|
+
ips_to_insert = set(DEFAULT_IPS) - existing_ips
|
|
389
|
+
|
|
390
|
+
if not ips_to_insert:
|
|
391
|
+
print(" * [WafaHell] Whitelist padrão já está atualizada.")
|
|
392
|
+
return
|
|
393
|
+
|
|
394
|
+
# 3. Prepara Bulk Insert e Cache
|
|
395
|
+
now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
396
|
+
bulk_data = []
|
|
397
|
+
|
|
398
|
+
for ip in ips_to_insert:
|
|
399
|
+
bulk_data.append({
|
|
400
|
+
"ip": ip,
|
|
401
|
+
"added_at": now_str
|
|
402
|
+
})
|
|
403
|
+
# Já coloca no cache para funcionar imediatamente
|
|
404
|
+
waf_cache.set(f"whitelist_{ip}", True, expire=3600)
|
|
405
|
+
|
|
406
|
+
# 4. Grava no Banco
|
|
407
|
+
session.bulk_insert_mappings(Whitelist, bulk_data)
|
|
408
|
+
session.commit()
|
|
409
|
+
|
|
410
|
+
print(f" * [WafaHell] Seed Whitelist: {len(ips_to_insert)} IPs padrão adicionados.")
|
|
411
|
+
|
|
412
|
+
except Exception as e:
|
|
413
|
+
session.rollback()
|
|
414
|
+
print(f"Erro ao semear whitelist: {e}")
|
|
415
|
+
finally:
|
|
416
|
+
session.close()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
wafaHell/__init__.py,sha256=bdafr3LRK7_frr-V6umEJZUqt8RMrOiLJy72DSQfs2o,60
|
|
2
|
+
wafaHell/app.py,sha256=oZCqS6rwvm3vU1pc56yYiNc_Mh4DOI52H00qnVovt-o,986
|
|
3
|
+
wafaHell/globals.py,sha256=keIe0YCNsb5si0yUhUjd6nlk1s0YgfH5G7lH5-05Zws,160
|
|
4
|
+
wafaHell/logger.py,sha256=_h4ceGM2UYnEQ5SY7nbYtR0rZprCOC0rN1IehtkkM_o,5734
|
|
5
|
+
wafaHell/middleware.py,sha256=DlSGA-1YHaHG-iQ2lBIWJIuzQnxkRITBPMSQogM8hdA,19625
|
|
6
|
+
wafaHell/mock.py,sha256=EgfSCKYV3rUTerKoEyFQPSf47wrOo-E0yqe7JpQUe3w,2105
|
|
7
|
+
wafaHell/model.py,sha256=o3wrIp8H17vlIB1tW28ApYedXtJLIGTbtrB7zKjzbtE,2919
|
|
8
|
+
wafaHell/panel.py,sha256=mq_vUg4LWLaFEWLvXpuDB3_QUw46WHhw3ruKb6hc_Sc,15926
|
|
9
|
+
wafaHell/rateLimiter.py,sha256=p4IDxha-ZrRPROFf8Fa1gRTbbUTQdbD9TgBTmyMR2hQ,664
|
|
10
|
+
wafaHell/utils.py,sha256=nQk1A4J1OeuvDnX5lj8pIE8IyUF5f5wbtXt0XSdsF7A,16810
|
|
11
|
+
wafahell-1.1.0.dist-info/licenses/LICENSE,sha256=6bv9v4HamenV3rqm3mhaGOecwGFrgxtVTW7JPfFDmeY,1086
|
|
12
|
+
wafahell-1.1.0.dist-info/METADATA,sha256=8fF9VBaal86TMs5EwmntQeKPA7wY9bNbo-9VPvEWBEw,2087
|
|
13
|
+
wafahell-1.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
14
|
+
wafahell-1.1.0.dist-info/top_level.txt,sha256=VGBo2g3pOeTH2qIXfZDJCSblJgijemMHUHmI0bBgrls,9
|
|
15
|
+
wafahell-1.1.0.dist-info/RECORD,,
|
wafahell-1.0.0.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
wafaHell/__init__.py,sha256=bdafr3LRK7_frr-V6umEJZUqt8RMrOiLJy72DSQfs2o,60
|
|
2
|
-
wafaHell/app.py,sha256=mXUCyOVrbU2Cdugh-UIDSQf-6zqvT2aazZtJQp_KKJI,980
|
|
3
|
-
wafaHell/globals.py,sha256=keIe0YCNsb5si0yUhUjd6nlk1s0YgfH5G7lH5-05Zws,160
|
|
4
|
-
wafaHell/logger.py,sha256=keT-Iw6g9w1dtrk0GhldxhgKtmNUVClgFGkSkAWXS5Y,5686
|
|
5
|
-
wafaHell/middleware.py,sha256=cse81wj9ocUV11MPfrmvleD5nYq6asjDx6dqXToIcIs,14348
|
|
6
|
-
wafaHell/mock.py,sha256=EgfSCKYV3rUTerKoEyFQPSf47wrOo-E0yqe7JpQUe3w,2105
|
|
7
|
-
wafaHell/model.py,sha256=OB4sEucMSgO-DBLgLhqh0oBx5fZRug23MDnRc8vS5w4,2501
|
|
8
|
-
wafaHell/panel.py,sha256=NpbvgXy5Regm4dQDoMapr-otW27TvC7Q-vdPrZDD-5k,7465
|
|
9
|
-
wafaHell/rateLimiter.py,sha256=p4IDxha-ZrRPROFf8Fa1gRTbbUTQdbD9TgBTmyMR2hQ,664
|
|
10
|
-
wafaHell/utils.py,sha256=v9NXo94AQiHjjCOgC38vwrlLoQOCZqp_qOjZVz00NEI,12750
|
|
11
|
-
wafahell-1.0.0.dist-info/licenses/LICENSE,sha256=6bv9v4HamenV3rqm3mhaGOecwGFrgxtVTW7JPfFDmeY,1086
|
|
12
|
-
wafahell-1.0.0.dist-info/METADATA,sha256=2EA_4vTwwoXimX_etFdZo_x_Z2_qijnbSjuIHfwE10Q,2087
|
|
13
|
-
wafahell-1.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
14
|
-
wafahell-1.0.0.dist-info/top_level.txt,sha256=VGBo2g3pOeTH2qIXfZDJCSblJgijemMHUHmI0bBgrls,9
|
|
15
|
-
wafahell-1.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|