Implements IPIntel, Panic Bunker, and custom access control (#2436)

What it says on the tin.

IPIntel and BYOND account age relating panic bunker settings from TG. Also implements methods for potentially tagging VM users. Ontop of all of this, creates a spiffy UI for admins with R_SERVER to adjust the settings of the entire suite at runtime.
This commit is contained in:
skull132
2017-05-26 13:26:58 +03:00
committed by GitHub
parent c27866e4af
commit 7039840323
18 changed files with 492 additions and 72 deletions
+27 -1
View File
@@ -135,7 +135,33 @@
update_connection_data(C)
return
var/list/conn_info = json_decode(data)
var/list/data_object = json_decode(data)
if (!data_object || !data_object.len)
log_debug("CONN DATA: [C.ckey] has no connection data to showcase.")
return
if (data_object["vms"])
var/A = data_object["vms"]
var/count = 0
if (A & 1)
count++
if (A & 2)
count++
if (config.access_deny_vms && count >= config.access_deny_vms)
log_access("Failed Login: [C.ckey] [C.address] [C.computer_id] - Matching [count]/[config.access_deny_vms] VM identifiers. IDs: [A].", ckey = C.ckey)
message_admins("Failed Login: [C.ckey] [C.address] [C.computer_id] - Matching [count]/[config.access_deny_vms] VM identifiers. IDs: [A].")
spawn(20)
if (C)
del(C)
return
if (config.access_warn_vms && count >= config.access_warn_vms)
log_access("Notice: [key_name(C)] [C.address] [C.computer_id] - Matching [count] VM identifiers. IDs: [A].", ckey = C.ckey)
message_admins("Notice: [key_name(C)] [C.address] [C.computer_id] - Matching [count] VM identifiers. IDs: [A].")
var/list/conn_info = data_object["conn"]
if (!conn_info || !conn_info.len)
return
+2 -1
View File
@@ -169,7 +169,8 @@ var/list/admin_verbs_server = list(
/client/proc/admin_edit_motd,
/client/proc/toggle_recursive_explosions,
/client/proc/restart_controller,
/client/proc/cmd_ss_panic
/client/proc/cmd_ss_panic,
/client/proc/configure_access_control
)
var/list/admin_verbs_debug = list(
/client/proc/getruntimelog, // allows us to access runtime logs to somebody,
+129
View File
@@ -0,0 +1,129 @@
/datum/ipintel
var/ip
var/intel = 0
var/cache = FALSE
var/cacheminutesago = 0
var/cachedate = ""
var/cacherealtime = 0
/datum/ipintel/New()
cachedate = SQLtime()
cacherealtime = world.realtime
/datum/ipintel/proc/is_valid()
. = FALSE
if (intel < 0)
return
if (intel <= config.ipintel_rating_bad)
if (world.realtime < cacherealtime+(config.ipintel_save_good*60*60*10))
return TRUE
else
if (world.realtime < cacherealtime+(config.ipintel_save_bad*60*60*10))
return TRUE
/proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE)
var/datum/ipintel/res = new()
res.ip = ip
. = res
if (!ip || !config.ipintel_email || !SSipintel.enabled)
return
if (!bypasscache)
var/datum/ipintel/cachedintel = SSipintel.cache[ip]
if (cachedintel && cachedintel.is_valid())
cachedintel.cache = TRUE
return cachedintel
if(establish_db_connection(dbcon))
var/DBQuery/query_get_ip_intel = dbcon.NewQuery({"
SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW())
FROM ss13_ipintel
WHERE
ip = INET6_ATON('[ip]')
AND ((
intel < [config.ipintel_rating_bad]
AND
date + INTERVAL [config.ipintel_save_good] HOUR > NOW()
) OR (
intel >= [config.ipintel_rating_bad]
AND
date + INTERVAL [config.ipintel_save_bad] HOUR > NOW()
))
"})
if(!query_get_ip_intel.Execute())
return
if (query_get_ip_intel.NextRow())
res.cache = TRUE
res.cachedate = query_get_ip_intel.item[1]
res.intel = text2num(query_get_ip_intel.item[2])
res.cacheminutesago = text2num(query_get_ip_intel.item[3])
res.cacherealtime = world.realtime - (text2num(query_get_ip_intel.item[3])*10*60)
SSipintel.cache[ip] = res
return
res.intel = ip_intel_query(ip)
if (updatecache && res.intel >= 0)
SSipintel.cache[ip] = res
if(establish_db_connection(dbcon))
var/DBQuery/query_add_ip_intel = dbcon.NewQuery("INSERT INTO ss13_ipintel (ip, intel) VALUES (INET6_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()")
query_add_ip_intel.Execute()
/proc/ip_intel_query(ip, var/retryed=0)
. = -1 //default
if (!ip)
return
if (SSipintel.throttle > world.timeofday)
return
if (!SSipintel.enabled)
return
var/list/http[] = world.Export("http://[config.ipintel_domain]/check.php?ip=[ip]&contact=[config.ipintel_email]&format=json&flags=f")
if (http)
var/status = text2num(http["STATUS"])
if (status == 200)
var/response = json_decode(file2text(http["CONTENT"]))
if (response)
if (response["status"] == "success")
var/intelnum = text2num(response["result"])
if (isnum(intelnum))
return text2num(response["result"])
else
ipintel_handle_error("Bad intel from server: [response["result"]].", ip, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
else
ipintel_handle_error("Bad response from server: [response["status"]].", ip, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
else if (status == 429)
ipintel_handle_error("Error #429: We have exceeded the rate limit.", ip, 1)
return
else
ipintel_handle_error("Unknown status code: [status].", ip, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
else
ipintel_handle_error("Unable to connect to API.", ip, retryed)
if (!retryed)
sleep(25)
return .(ip, 1)
/proc/ipintel_handle_error(error, ip, retryed)
if (retryed)
SSipintel.errors++
error += " Could not check [ip]. Disabling IPINTEL for [SSipintel.errors] minute[( SSipintel.errors == 1 ? "" : "s" )]"
SSipintel.throttle = world.timeofday + (10 * 120 * SSipintel.errors)
else
error += " Attempting retry on [ip]."
log_ipintel(error)
/proc/log_ipintel(text)
log_game("IPINTEL: [text]")
log_debug("IPINTEL: [text]")
+4
View File
@@ -1606,6 +1606,10 @@
return
else if(href_list["access_control"])
access_control_topic(href_list["access_control"])
return
mob/living/proc/can_centcom_reply()
return 0
@@ -0,0 +1,99 @@
/client/proc/configure_access_control()
set category = "Server"
set name = "Configure Access Control"
if (!check_rights(R_SERVER))
return
var/datum/browser/config_window = new(usr, "access_control", "Access Control")
config_window.add_head_content("<title>Access Control</title>")
var/data = "These settings control who can access the server during this round.<br>"
data += "They must be reset every single time the server restarts.<br>"
data += "<b>If you do not know what these do, you shouldn't be touching them!</b><hr>"
data += "<h2>IP Intel Settings:</h2><br><ul>"
data += "<li>Current warning level: [config.ipintel_rating_bad ? "[config.ipintel_rating_bad]" : "<font color='red'>DISABLED</font>"]. <a href='?_src_=holder;access_control=intel_bad'>Edit</a></li>"
data += "<li>Current kick level: [config.ipintel_rating_kick ? "[config.ipintel_rating_kick]" : "<font color='red'>DISABLED</font>"]. <a href='?_src_=holder;access_control=intel_kick'>Edit</a></li>"
data += "</ul><hr>"
data += "<h2>Player Age Settings:</h2><br><ul>"
data += "<li>New players: [config.access_deny_new_players ? "<font color='green'>ALLOWED</font>" : "<font color='red'>DENIED</font>"]. <a href='?_src_=holder;access_control=new_players;'>Toggle</a></li>"
data += "<li>Account age restriction: [config.access_deny_new_accounts == -1 ? "<font color='red'>DISABLED</font>" : "[config.access_deny_new_accounts] DAYS"]. <a href='?_src_=holder;access_control=new_accounts;'>Edit</a></li>"
data += "</ul><hr>"
data += "<h2>VM Detection Settings:</h2><br><ul>"
data += "<li>VM identifier count to warn on: [config.access_warn_vms ? "[config.access_warn_vms]" : "<font color='red'>DISABLED</font>"]. <a href='?_src_=holder;access_control=vm_warn;'>Edit</a></li>"
data += "<li>VM identifier count to kick on: [config.access_deny_vms ? "[config.access_deny_vms]" : "<font color='red'>DISABLED</font>"]. <a href='?_src_=holder;access_control=vm_kick;'>Edit</a></li>"
data += "</ul>"
config_window.set_user(src.mob)
config_window.set_content(data)
config_window.open()
/datum/admins/proc/access_control_topic(control)
if (!control)
to_chat(usr, "<span class='warning'>No control option sent. Cancelling.</span>")
return
if (!check_rights(R_SERVER))
log_and_message_admins("has tried editing access control without the permissions to do so.")
return
switch(control)
if ("intel_bad")
var/num = input("Please set the new threshold for warning based on IPintel (0 to disable).", "New Threshold", config.ipintel_rating_kick) as num
if (num < 0 || num > 1)
to_chat(usr, "<span class='warning'>Invalid number. Cancelling.</span>")
return
config.ipintel_rating_bad = num
if (num)
log_and_message_admins("has set the IPIntel warn threshold to [num].")
else
log_and_message_admins("has disabled warn based on IPIntel.")
if ("intel_kick")
var/num = input("Please set the new threshold for kicking based on IPintel (0 to disable).", "New Threshold", config.ipintel_rating_kick) as num
if (num < 0 || num > 1)
to_chat(usr, "<span class='warning'>Invalid number. Cancelling.</span>")
return
config.ipintel_rating_kick = num
if (num)
log_and_message_admins("has set the IPIntel kick threshold to [num].")
else
log_and_message_admins("has disabled kicking based on IPIntel.")
if ("new_players")
config.access_deny_new_players = !config.access_deny_new_players
log_and_message_admins("has [config.access_deny_new_players ? "ENABLED" : "DISABLED"] the kicking of new players.")
if ("new_accounts")
var/num = input("Please set the new threshold for denying access based on BYOND account age. (-1 to disable.)", "New Threshold", config.access_deny_new_accounts) as num
if (num < 0 && num != -1)
to_chat(usr, "<span class='warning'>Invalid number. Cancelling.</span>")
return
config.access_deny_new_accounts = num
if (num != -1)
log_and_message_admins("has set the access barrier for new BYOND accounts to [num] days.")
else
log_and_message_admins("has disabled kicking based on BYOND account age.")
if ("vm_warn")
var/num = input("Please set the new threshold for warning on login based on positive VM identifiers. (0 to disable.)", "New Threshold") in list(0, 1, 2)
config.access_warn_vms = num
if (num)
log_and_message_admins("has set players with [config.access_warn_vms] positive identifiers out of 2 for VM usage to be warned.")
else
log_and_message_admins("has disabled warnings based on potential VM usage.")
if ("vm_kick")
var/num = input("Please set the new threshold for warning on login based on positive VM identifiers. (0 to disable.)", "New Threshold") in list(0, 1, 2)
config.access_deny_vms = num
if (num)
log_and_message_admins("has set players with [config.access_deny_vms] positive identifiers out of 2 for VM usage to be warned.")
else
log_and_message_admins("has disabled warnings based on potential VM usage.")
else
to_chat(usr, "<span class='danger'>Unknown control message sent. Cancelling.</span>")
owner.configure_access_control()
@@ -9,6 +9,19 @@ var/list/VVlocked = list("vars", "holder", "client", "virus", "viruses", "cuffed
var/list/VVicon_edit_lock = list("icon", "icon_state", "overlays", "underlays")
var/list/VVckey_edit = list("key", "ckey")
// The paranoia box. Specify a var name => bitflags required to edit it.
// Allows the securing of specific variables for, for example, config. That alter
// how players could join! Definitely not something you want folks to touch if they
// don't have the perms.
var/list/VVdynamic_lock = list(
"access_deny_new_players" = R_SERVER,
"access_deny_new_accounts" = R_SERVER,
"access_deny_vms" = R_SERVER,
"access_warn_vms" = R_SERVER,
"ipintel_rating_bad" = R_SERVER,
"ipintel_rating_kick" = R_SERVER
)
/*
/client/proc/cmd_modify_object_variables(obj/O as obj|mob|turf|area in world) // Acceptable 'in world', as VV would be incredibly hampered otherwise
set category = "Debug"
@@ -171,6 +184,8 @@ var/list/VVckey_edit = list("key", "ckey")
if(!check_rights(R_SPAWN|R_DEBUG|R_DEV)) return
if(variable in VVicon_edit_lock)
if(!check_rights(R_FUN|R_DEBUG|R_DEV)) return
if(VVdynamic_lock[variable])
if(!check_rights(VVdynamic_lock[variable])) return
if(isnull(variable))
usr << "Unable to determine variable type."
@@ -364,6 +379,8 @@ var/list/VVckey_edit = list("key", "ckey")
if(!check_rights(R_SPAWN|R_DEBUG|R_DEV)) return
if(param_var_name in VVicon_edit_lock)
if(!check_rights(R_FUN|R_DEBUG|R_DEV)) return
if(VVdynamic_lock[variable])
if(!check_rights(VVdynamic_lock[variable])) return
variable = param_var_name
@@ -426,6 +443,8 @@ var/list/VVckey_edit = list("key", "ckey")
if(!check_rights(R_SPAWN|R_DEBUG|R_DEV)) return
if(variable in VVicon_edit_lock)
if(!check_rights(R_FUN|R_DEBUG|R_DEV)) return
if(VVdynamic_lock[variable])
if(!check_rights(VVdynamic_lock[variable])) return
if(!autodetect_class)
+2 -1
View File
@@ -197,6 +197,7 @@ proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0)
if(M.client.prefs.muted & mute_type)
muteunmute = "unmuted"
M.client.prefs.muted &= ~mute_type
M.client.spam_alert = 0
else
muteunmute = "muted"
M.client.prefs.muted |= mute_type
@@ -623,7 +624,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
if (!holder)
src << "Only administrators may use this command."
return
for(var/datum/job/job in SSjobs.occupations)
src << "[job.title]: [job.total_positions]"
feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!