[#1460] Add "service" field in bans and reports

This commit is contained in:
Théophile Diot 2024-10-23 11:38:13 +02:00
parent 609dfc3cdc
commit 0d6fdbdd18
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
11 changed files with 56 additions and 26 deletions

View file

@ -225,6 +225,7 @@ api.global.POST["^/ban$"] = function(self)
ip = "",
exp = 86400,
reason = "manual",
service = "unknown",
}
ban.ip = ip["ip"]
if ip["exp"] then
@ -233,10 +234,14 @@ api.global.POST["^/ban$"] = function(self)
if ip["reason"] then
ban.reason = ip["reason"]
end
if ip["service"] then
ban.service = ip["service"]
end
datastore:set(
"bans_ip_" .. ban["ip"],
encode({
reason = ban["reason"],
service = ban["service"],
date = os.time(),
}),
ban["exp"]
@ -267,12 +272,15 @@ api.global.GET["^/bans$"] = function(self)
local ban_data
ok, ban_data = pcall(decode, result)
if not ok then
ban_data = { reason = result, date = -1 }
ban_data = { reason = result, service = "unknown", date = -1 }
end
table.insert(
data,
{ ip = k:sub(9, #k), reason = ban_data["reason"], date = ban_data["date"], exp = math.floor(ttl) }
)
table.insert(data, {
ip = k:sub(9, #k),
reason = ban_data["reason"],
service = ban_data["service"],
date = ban_data["date"],
exp = math.floor(ttl),
})
end
end
return self:response(HTTP_OK, "success", data)

View file

@ -714,10 +714,11 @@ utils.is_banned = function(ip)
return false, "not banned"
end
utils.add_ban = function(ip, reason, ttl)
utils.add_ban = function(ip, reason, ttl, service)
-- Set on local datastore
local ban_data = encode({
reason = reason,
service = service or "unknown",
date = os.time(),
})
local ok, err = datastore:set("bans_ip_" .. ip, ban_data, ttl)

View file

@ -208,7 +208,7 @@ class CLI(ApiCaller):
def ban(self, ip: str, exp: float, reason: str) -> Tuple[bool, str]:
if self.__redis:
try:
ok = self.__redis.set(f"bans_ip_{ip}", dumps({"reason": reason, "date": time()}))
ok = self.__redis.set(f"bans_ip_{ip}", dumps({"reason": reason, "date": time(), "service": "bwcli"}))
if not ok:
self.__logger.error(f"Failed to ban {ip} in redis")
self.__redis.expire(f"bans_ip_{ip}", int(exp))
@ -260,7 +260,12 @@ class CLI(ApiCaller):
banned_date = f"the {datetime.fromtimestamp(ban['date']).strftime('%Y-%m-%d at %H:%M:%S %Z')} "
if ban["exp"] != -1:
remaining = f"for {format_remaining_time(ban['exp'])} remaining"
cli_str += f"- {ban['ip']} ; banned {banned_date}{remaining} with reason \"{ban.get('reason', 'no reason given')}\"\n"
cli_str += f"- {ban['ip']} ; banned {banned_date}{remaining} with reason \"{ban.get('reason', 'no reason given')}\""
if ban.get("service", "unknown") != "unknown":
cli_str += f" by {ban['service'] if ban['service'] != '_' else 'default server'}"
cli_str += "\n"
cli_str += "\n"
return True, cli_str

View file

@ -44,7 +44,8 @@ function badbehavior:log()
tonumber(self.variables["BAD_BEHAVIOR_COUNT_TIME"]),
tonumber(self.variables["BAD_BEHAVIOR_BAN_TIME"]),
tonumber(self.variables["BAD_BEHAVIOR_THRESHOLD"]),
self.use_redis
self.use_redis,
self.ctx.bw.server_name
)
if not ok then
return self:ret(false, "can't create increase timer : " .. err)
@ -62,7 +63,7 @@ function badbehavior:log_stream()
end
-- luacheck: ignore 212
function badbehavior.increase(premature, ip, count_time, ban_time, threshold, use_redis)
function badbehavior.increase(premature, ip, count_time, ban_time, threshold, use_redis, server_name)
-- Instantiate objects
local logger = require "bunkerweb.logger":new("badbehavior")
local datastore = require "bunkerweb.datastore":new()
@ -102,7 +103,7 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
end
-- Store local ban
if counter > threshold then
ok, err = add_ban(ip, "bad behavior", ban_time)
ok, err = add_ban(ip, "bad behavior", ban_time, server_name)
if not ok then
logger:log(ERR, "(increase) can't save ban : " .. err)
return

View file

@ -72,6 +72,7 @@ function metrics:log(bypass_checks)
status = ngx.status,
user_agent = self.ctx.bw.http_user_agent or "",
reason = reason,
server_name = self.ctx.bw.server_name,
data = data,
}
-- Get current requests

View file

@ -106,9 +106,9 @@ class Instance:
return f"Instance {self.hostname} has been restarted."
return f"Can't restart instance {self.hostname}"
def ban(self, ip: str, exp: float, reason: str) -> str:
def ban(self, ip: str, exp: float, reason: str, service: str) -> str:
try:
result = self.apiCaller.send_to_apis("POST", "/ban", data={"ip": ip, "exp": exp, "reason": reason})[0]
result = self.apiCaller.send_to_apis("POST", "/ban", data={"ip": ip, "exp": exp, "reason": reason, "service": service})[0]
except BaseException as e:
return f"Can't ban {ip} on instance {self.hostname}: {e}"
@ -193,8 +193,10 @@ class InstancesUtils:
instance.name for instance in instances or self.get_instances() if instance.status == "down" or instance.reload().startswith("Can't reload")
] or "Successfully reloaded instances"
def ban(self, ip: str, exp: float, reason: str, *, instances: Optional[List[Instance]] = None) -> Union[list[str], str]:
return [instance.name for instance in instances or self.get_instances(status="up") if instance.ban(ip, exp, reason).startswith("Can't ban")] or ""
def ban(self, ip: str, exp: float, reason: str, service: str, *, instances: Optional[List[Instance]] = None) -> Union[list[str], str]:
return [
instance.name for instance in instances or self.get_instances(status="up") if instance.ban(ip, exp, reason, service).startswith("Can't ban")
] or ""
def unban(self, ip: str, *, instances: Optional[List[Instance]] = None) -> Union[list[str], str]:
return [instance.name for instance in instances or self.get_instances(status="up") if instance.unban(ip).startswith("Can't unban")] or ""

View file

@ -85,12 +85,12 @@ def bans_ban():
ban_end = (ban_end - current_time).total_seconds()
if redis_client:
ok = redis_client.set(f"bans_ip_{ban['ip']}", dumps({"reason": reason, "date": time()}))
ok = redis_client.set(f"bans_ip_{ban['ip']}", dumps({"reason": reason, "date": time(), "service": "web UI"}))
if not ok:
flash(f"Couldn't ban {ban['ip']} on redis", "error")
redis_client.expire(f"bans_ip_{ban['ip']}", int(ban_end))
resp = BW_INSTANCES_UTILS.ban(ban["ip"], ban_end, reason)
resp = BW_INSTANCES_UTILS.ban(ban["ip"], ban_end, reason, "web UI")
if resp:
flash(f"Couldn't ban {ban['ip']} on the following instances: {', '.join(resp)}", "error")
else:

View file

@ -150,7 +150,7 @@ $(document).ready(function () {
viewTotal: true,
cascadePanes: true,
collapse: false,
columns: [1, 4],
columns: [1, 4, 5],
},
},
topStart: {},
@ -336,7 +336,7 @@ $(document).ready(function () {
targets: -1,
},
{
targets: [1, 4],
targets: [1, 5],
render: function (data, type, row) {
if (type === "display" || type === "filter") {
const date = new Date(data);
@ -396,7 +396,7 @@ $(document).ready(function () {
{
label: "Next 24 hours",
value: function (rowData, rowIdx) {
const date = new Date(rowData[4]);
const date = new Date(rowData[5]);
const now = new Date();
return date - now < 24 * 60 * 60 * 1000;
},
@ -404,7 +404,7 @@ $(document).ready(function () {
{
label: "Next 7 days",
value: function (rowData, rowIdx) {
const date = new Date(rowData[4]);
const date = new Date(rowData[5]);
const now = new Date();
return date - now < 7 * 24 * 60 * 60 * 1000;
},
@ -412,7 +412,7 @@ $(document).ready(function () {
{
label: "Next 30 days",
value: function (rowData, rowIdx) {
const date = new Date(rowData[4]);
const date = new Date(rowData[5]);
const now = new Date();
return date - now < 30 * 24 * 60 * 60 * 1000;
},
@ -420,7 +420,7 @@ $(document).ready(function () {
{
label: "More than 30 days",
value: function (rowData, rowIdx) {
const date = new Date(rowData[4]);
const date = new Date(rowData[5]);
const now = new Date();
return date - now >= 30 * 24 * 60 * 60 * 1000;
},
@ -429,10 +429,14 @@ $(document).ready(function () {
combiner: "or",
orderable: false,
},
targets: 5,
},
{
searchPanes: { show: true },
targets: 4,
},
],
order: [[4, "asc"]],
order: [[5, "asc"]],
autoFill: false,
responsive: true,
select: {

View file

@ -301,7 +301,7 @@ $(function () {
viewTotal: true,
cascadePanes: true,
collapse: false,
columns: [1, 2, 3, 4, 5, 7],
columns: [1, 2, 3, 4, 5, 7, 8],
},
},
topStart: {},
@ -460,7 +460,7 @@ $(function () {
},
{
searchPanes: { show: true },
targets: [1, 2, 3, 4, 5, 7],
targets: [1, 2, 3, 4, 5, 7, 8],
},
],
order: [[0, "desc"]],

View file

@ -28,6 +28,9 @@
<th data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="The reason why the Report was created">Reason</th>
<th data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="The service that created the ban">Service</th>
<th data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="The end date of the Ban">End date</th>
@ -46,6 +49,7 @@
<td class="ban-start-date">{{ ban["start_date"] }}</td>
<td>{{ ban["ip"] }}</td>
<td>{{ ban["reason"] }}</td>
<td>{{ ban["service"] if ban["service"] != "_" else "default server" }}</td>
<td class="ban-end-date">{{ ban["end_date"] }}</td>
<td>{{ ban["remain"] }}</td>
<td>

View file

@ -34,6 +34,9 @@
<th data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="The reason why the Report was created">Reason</th>
<th data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="The Server name that created the report">Server name</th>
<th data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="Additional data about the Report">Data</th>
@ -69,6 +72,7 @@
<td>{{ report["status"] }}</td>
<td>{{ report["user_agent"] }}</td>
<td>{{ report["reason"] }}</td>
<td>{{ report["server_name"] }}</td>
<td>{{ report["data"] }}</td>
</tr>
{% endfor %}