mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2025-12-22 08:01:06 +00:00
119 lines
3.3 KiB
Plaintext
119 lines
3.3 KiB
Plaintext
var/datum/controller/subsystem/fail2topic/SSfail2topic
|
|
|
|
/datum/controller/subsystem/fail2topic
|
|
name = "Fail2Topic"
|
|
init_order = SS_INIT_MISC_FIRST
|
|
flags = SS_FIRE_IN_LOBBY | SS_BACKGROUND
|
|
|
|
var/list/rate_limiting = list()
|
|
var/list/fail_counts = list()
|
|
var/list/active_bans = list()
|
|
|
|
var/rate_limit
|
|
var/max_fails
|
|
var/enabled = FALSE
|
|
|
|
/datum/controller/subsystem/fail2topic/New()
|
|
NEW_SS_GLOBAL(SSfail2topic)
|
|
|
|
/datum/controller/subsystem/fail2topic/Initialize(timeofday)
|
|
rate_limit = config.fail2topic_rate_limit
|
|
max_fails = config.fail2topic_max_fails
|
|
enabled = config.fail2topic_enabled
|
|
|
|
DropFirewallRule() // Clear the old bans if any still remain
|
|
|
|
|
|
if (world.system_type == UNIX && enabled)
|
|
enabled = FALSE
|
|
log_ss("fail2topic", "Subsystem disabled due to it not supporting UNIX.", log_world = FALSE, severity = SEVERITY_NOTICE)
|
|
|
|
if (!enabled)
|
|
suspended = TRUE
|
|
flags |= SS_NO_FIRE
|
|
|
|
. = ..()
|
|
|
|
/datum/controller/subsystem/fail2topic/fire()
|
|
while (rate_limiting.len)
|
|
var/ip = rate_limiting[1]
|
|
var/last_attempt = rate_limiting[ip]
|
|
|
|
if (world.time - last_attempt > rate_limit)
|
|
rate_limiting -= ip
|
|
fail_counts -= ip
|
|
|
|
if (MC_TICK_CHECK)
|
|
return
|
|
|
|
/datum/controller/subsystem/fail2topic/Shutdown()
|
|
DropFirewallRule()
|
|
|
|
/datum/controller/subsystem/fail2topic/proc/IsRateLimited(ip)
|
|
var/last_attempt = rate_limiting[ip]
|
|
|
|
if (config?.api_rate_limit_whitelist[ip])
|
|
return FALSE
|
|
|
|
if (active_bans[ip])
|
|
return TRUE
|
|
|
|
rate_limiting[ip] = world.time
|
|
|
|
if (isnull(last_attempt))
|
|
return FALSE
|
|
|
|
if (world.time - last_attempt > rate_limit)
|
|
fail_counts -= ip
|
|
return FALSE
|
|
else
|
|
var/failures = fail_counts[ip]
|
|
|
|
if (isnull(failures))
|
|
fail_counts[ip] = 1
|
|
return TRUE
|
|
else if (failures > max_fails)
|
|
BanFromFirewall(ip)
|
|
return TRUE
|
|
else
|
|
fail_counts[ip] = failures + 1
|
|
return TRUE
|
|
|
|
/datum/controller/subsystem/fail2topic/proc/BanFromFirewall(ip)
|
|
if (!enabled)
|
|
return
|
|
var/static/regex/R = regex(@"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") // Anything that interacts with a shell should be parsed. Prevents subnet banning and possible injection vulnerabilities
|
|
R.Find(ip)
|
|
ip = R.match
|
|
if(length(ip) > 15 || length(ip) < 8)
|
|
WARNING("BanFromFirewall was called with an invalid or unsafe IP")
|
|
return FALSE
|
|
|
|
active_bans[ip] = world.time
|
|
fail_counts -= ip
|
|
rate_limiting -= ip
|
|
|
|
. = shell("netsh advfirewall firewall add rule name=\"[config.fail2topic_rule_name]\" dir=in interface=any action=block remoteip=[ip]")
|
|
|
|
if (.)
|
|
log_ss("fail2topic", "Failed to ban [ip]. Exit code: [.].", log_world = FALSE, severity = SEVERITY_ERROR)
|
|
else if (isnull(.))
|
|
log_ss("fail2topic", "Failed to invoke ban script.", log_world = FALSE, severity = SEVERITY_ERROR)
|
|
else
|
|
log_ss("fail2topic", "Banned [ip].", log_world = FALSE, severity = SEVERITY_NOTICE)
|
|
|
|
/datum/controller/subsystem/fail2topic/proc/DropFirewallRule()
|
|
if (!enabled)
|
|
return
|
|
|
|
active_bans = list()
|
|
|
|
. = shell("netsh advfirewall firewall delete rule name=\"[config.fail2topic_rule_name]\"")
|
|
|
|
if (.)
|
|
log_ss("fail2topic", "Failed to drop firewall rule. Exit code: [.].", log_world = FALSE, severity = SEVERITY_ERROR)
|
|
else if (isnull(.))
|
|
log_ss("fail2topic", "Failed to invoke ban script.", log_world = FALSE, severity = SEVERITY_ERROR)
|
|
else
|
|
log_ss("fail2topic", "Firewall rule dropped.", log_world = FALSE, severity = SEVERITY_INFO)
|