From e67419d8ccc5a7ea0097805dfce77380de98fa98 Mon Sep 17 00:00:00 2001 From: Aronai Sieyes Date: Wed, 15 Jan 2020 14:30:41 -0500 Subject: [PATCH] IP reputation checking --- code/controllers/configuration.dm | 25 ++++++++ code/modules/client/client defines.dm | 1 + code/modules/client/client procs.dm | 87 ++++++++++++++++++++++++++- config/example/config.txt | 22 ++++++- 4 files changed, 132 insertions(+), 3 deletions(-) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index c3c6e4eca6e..bf48f4e8f87 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -104,6 +104,13 @@ var/list/gamemode_cache = list() var/panic_bunker = 0 var/paranoia_logging = 0 + var/ip_reputation = FALSE //Should we query IPs to get scores? Generates HTTP traffic to an API service. + var/ipr_email //Left null because you MUST specify one otherwise you're making the internet worse. + var/ipr_block_bad_ips = FALSE //Should we block anyone who meets the minimum score below? Otherwise we just log it (If paranoia logging is on, visibly in chat). + var/ipr_bad_score = 1 //The API returns a value between 0 and 1 (inclusive), with 1 being 'definitely VPN/Tor/Proxy'. Values equal/above this var are considered bad. + var/ipr_allow_existing = FALSE //Should we allow known players to use VPNs/Proxies? If the player is already banned then obviously they still can't connect. + var/ipr_minimum_age = 5 //How many days before a player is considered 'fine' for the purposes of allowing them to use VPNs. + var/serverurl var/server var/banappeals @@ -806,6 +813,24 @@ var/list/gamemode_cache = list() if ("paranoia_logging") config.paranoia_logging = 1 + if("ip_reputation") + config.ip_reputation = 1 + + if("ipr_email") + config.ipr_email = value + + if("ipr_block_bad_ips") + config.ipr_block_bad_ips = 1 + + if("ipr_bad_score") + config.ipr_bad_score = text2num(value) + + if("ipr_allow_existing") + config.ipr_allow_existing = 1 + + if("ipr_minimum_age") + config.ipr_minimum_age = text2num(value) + if("random_submap_orientation") config.random_submap_orientation = 1 diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index e711006b2ad..d624ec6f153 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -41,6 +41,7 @@ var/received_irc_pm = -99999 var/irc_admin //IRC admin that spoke with them last. var/mute_irc = 0 + var/ip_reputation = 0 //Do we think they're using a proxy/vpn? Only if IP Reputation checking is enabled in config. //////////////////////////////////// diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 90f8532ded9..739440ff8b5 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -282,7 +282,32 @@ qdel(src) return 0 - // VOREStation Edit Start - Department Hours + // IP Reputation Check + if(config.ip_reputation) + if(config.ipr_allow_existing && player_age >= config.ipr_minimum_age) + log_admin("Skipping IP reputation check on [key] with [address] because of player age") + else if(update_ip_reputation()) //It is set now + if(ip_reputation >= config.ipr_bad_score) //It's bad + + //Log it + if(config.paranoia_logging) //We don't block, but we want paranoia log messages + log_and_message_admins("[key] at [address] has bad IP reputation: [ip_reputation]. Will be kicked if enabled in config.") + else //We just log it + log_admin("[key] at [address] has bad IP reputation: [ip_reputation]. Will be kicked if enabled in config.") + + //Take action if required + if(config.ipr_block_bad_ips && config.ipr_allow_existing) //We allow players of an age, but you don't meet it + to_chat(src,"Sorry, we only allow VPN/Proxy/Tor usage for players who have spent at least [config.ipr_minimum_age] days on the server. If you are unable to use the internet without your VPN/Proxy/Tor, please contact an admin out-of-game to let them know so we can accomidate this.") + qdel(src) + return 0 + else if(config.ipr_block_bad_ips) //We don't allow players of any particular age + to_chat(src,"Sorry, we do not accept connections from users via VPN/Proxy/Tor connections.") + qdel(src) + return 0 + else + log_admin("Couldn't perform IP check on [key] with [address]") + + // VOREStation Edit Start - Department Hoursn if(config.time_off) var/DBQuery/query_hours = dbcon.NewQuery("SELECT department, hours FROM vr_player_hours WHERE ckey = '[sql_ckey]'") query_hours.Execute() @@ -415,3 +440,63 @@ client/verb/character_setup() if(var_name == NAMEOF(src, holder)) return FALSE return ..() + +//This is for getipintel.net. +//You're welcome to replace this proc with your own that does your own cool stuff. +//Just set the client's ip_reputation var and make sure it makes sense with your config settings (higher numbers are worse results) +/client/proc/update_ip_reputation() + var/request = "http://check.getipintel.net/check.php?ip=[address]&contact=[config.ipr_email]" + var/http[] = world.Export(request) + + /* Debug + world.log << "Requested this: [request]" + for(var/entry in http) + world.log << "[entry] : [http[entry]]" + */ + + if(!http || !islist(http)) //If we couldn't check, the service might be down, fail-safe. + log_admin("Couldn't connect to getipintel.net to check [address] for [key]") + return FALSE + + //429 is rate limit exceeded + if(text2num(http["STATUS"]) == 429) + log_and_message_admins("getipintel.net reports HTTP status 429. IP reputation checking is now disabled. If you see this, let a developer know.") + config.ip_reputation = FALSE + return FALSE + + var/content = file2text(http["CONTENT"]) //world.Export actually returns a file object in CONTENT + var/score = text2num(content) + if(isnull(score)) + return FALSE + + //Error handling + if(score < 0) + var/fatal = TRUE + var/ipr_error = "getipintel.net IP reputation check error while checking [address] for [key]: " + switch(score) + if(-1) + ipr_error += "No input provided" + if(-2) + fatal = FALSE + ipr_error += "Invalid IP provided" + if(-3) + fatal = FALSE + ipr_error += "Unroutable/private IP (spoofing?)" + if(-4) + fatal = FALSE + ipr_error += "Unable to reach database" + if(-5) + ipr_error += "Our IP is banned or otherwise forbidden" + if(-6) + ipr_error += "Missing contact info" + + log_and_message_admins(ipr_error) + if(fatal) + config.ip_reputation = FALSE + log_and_message_admins("With this error, IP reputation checking is disabled for this shift. Let a developer know.") + return FALSE + + //Went fine + else + ip_reputation = score + return TRUE diff --git a/config/example/config.txt b/config/example/config.txt index 434bd2233db..68b06f82004 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -460,8 +460,26 @@ RADIATION_RESISTANCE_MULTIPLIER 2.1 ## Divisor for material weights when computing radiation resistance of a material object (walls) RADIATION_MATERIAL_RESISTANCE_DIVISOR 16 - ## Mode of computing radiation resistance into effective radiation on a turf ## One and only one of the following options must be uncommented RADIATION_RESISTANCE_CALC_DIVIDE -# RADIATION_RESISTANCE_CALC_SUBTRACT \ No newline at end of file +# RADIATION_RESISTANCE_CALC_SUBTRACT + +## IP Reputation Checking +# Enable/disable IP reputation checking (present/nonpresent) +#IP_REPUTATION + +# Set the e-mail address problems can go to for IPR checks (e-mail address) +IPR_EMAIL whatever@whatever.com + +# Above this value, reputation scores are considered 'bad' (number) +IPR_BAD_SCORE 1 + +# If you want the people disconnected. Otherwise it just logs. (present/nonpresent) +IPR_BLOCK_BAD_IPS + +# If players of a certain length of playtime are allowed anyway (REQUIRES DATABASE) (present/nonpresent) +IPR_ALLOW_EXISTING + +# And what that age is (number) +IPR_MINIMUM_AGE 5