VPN Autoban


3D# General
Staff member
I figured that others might actually find some use in the VPN Autoban which 3D and 1fx uses as well. There's no point of keeping it hidden, as others might benefit from it as well.

Getting the IP's from the server and whatnot is the easy part, what I initally thought is the hard part, was somehow checking that is the IP a VPN or not, thankfully I stumbled across IPhub:
So what the small program below does, is just running running the command "rcon status", gets the output, parses out the ID, name and IP of the player and then checks the IP over either a local database or over IPhub. I added a local database just because the IPhub free API has a query limit + I wanted to keep the script runtime as low as possible. Query once, never query again. It also acts as a whitelist, if the IP is indeed deemed as a VPN/Hosting IP, I can just add it to the table so it becomes whitelisted.

Table structure for q3panel_allowed_ips:
Table structure for q3panel_banned_ips:

The blocklevel variable is either 0, 1 or 2:
block: 0 - Residential or business IP (i.e. safe IP)

block: 1 - Non-residential IP (hosting provider, proxy, etc.)

block: 2 - Non-residential & residential IP (warning, may flag innocent people)
The script relies on q3panel, but it's easy to actually make it run on any server and it doesn't even have to be hosted on the same machine where the server is. All it needs is a RCON password. Code is a bit messy as it was cooked together fast. FYI - you need the API key from IPhub and you need to paste it to $api_key variable.


$d = __DIR__;
require_once "$d/classes/sql/SQL.php";
require_once "$d/config.php";
require_once "$d/classes/Constants.php";
require_once "$d/classes/ssh/SSH.php";
require_once "$d/classes/servers/host/Host.php";
require_once "$d/classes/servers/Game.php";
require_once "$d/classes/servers/Server.php";
require_once "$d/classes/logger/Logger.php";
require_once "$d/classes/email/Email.php";
require_once "$d/classes/users/User.php";

$servers = Server::getRunningServers($sql);
$ips = "";
$srv = null;
foreach ($servers as $server) {
    if (intval($server['server_id']) !== 2) { //only 3D server.
    $host = new Host($server['host_id'], $server['servername'], $server['hostname'], $server['sshport'], $server['host_username'], $server['host_password']);
    $game = new Game($server['game_id'], $server['game_name'], $server['game_location'], $server['startscript']);
    $srv = new Server($server['server_id'], $host, $server['server_name'], $game, $server['server_port'], $server['server_account'], $server['server_password'], $server['server_status'], $server['server_startscript'], $server['current_players'], $server['max_players'], $server['rconpassword']);
    $str = $srv->sendQ3Command(Constants::$SERVER_ACTIONS['Q3_RCON_COMMAND'] . "status", true, true);
    $str = str_replace("\xFF\xFF\xFF\xFFprint\n", "", $str);
    $str = str_replace("\xFF\xFF\xFF\xFFprint", "", $str);
    $arr = explode("\n", $str);
    $players = false;
    foreach ($arr as $val) {
        if (strlen(trim($val)) === 0) {
        if (trim($val) === "--- ----- ---- --------------- ------- --------------------- ----- -----") {
            $players = true;
        if ($players) {
            $tt = preg_replace("!\s+!", " ", $val);
            $array = explode(" ", $tt);
            $id = intval($array[1]);
            $name = stripQ3Colors($array[4]);
            $last = "";
            $writeName = false;
            foreach ($array as $value) {
                if (stripQ3Colors($value) === $name) {
                    $writeName = true;
                $value = trim($value);
                $arr22 = explode(":", $value);
                $chkIp = $arr22[0];
                if ($out = filter_var($chkIp, FILTER_VALIDATE_IP)) {
                    $writeName = false;
                    $ips[] = array("ip" => $out, "id" => $id, "name" => $name);
                if ($writeName) {
                    $name .= stripQ3Colors($last);
                    $last = " $value";

$api_key = "*****"; //You need the API key from IPhub.
if (!is_array($ips) || sizeof($ips) === 0) {
foreach ($ips as $arr) {
    $ip = $arr['ip'];
    $name = $arr['name'];
    $id = $arr['id'];
    if (trim($ip) === "") {
    $query = "SELECT * FROM q3panel_banned_ips WHERE ip_address = ?";
    $params = array($ip);
    $dat = $sql->query($query, $params);
    if (sizeof($dat) !== 0) {
        $sqlId = $dat[0]['banned_ip_id'];
        $output = $srv->sendQ3Command(Constants::$SERVER_ACTIONS['Q3_RCON_COMMAND'] . "ban $id ID-$sqlId-VPN_Autoban", true, true);
    $query = "SELECT * FROM q3panel_allowed_ips WHERE ip_address = ?";
    $dat = $sql->query($query, $params);
    if (sizeof($dat) !== 0) {
//echo "ip $ip allowed"; 
    $ch = curl_init("http://v2.api.iphub.info/ip/" . trim($ip));
    curl_setopt_array($ch, array(
        CURLOPT_HTTPHEADER => array("X-Key: $api_key")
    $ret = curl_exec($ch);
    $jsonarr = json_decode($ret, true);
    $query = "";
    if (intval($jsonarr['block']) === 1) {
        $query = "INSERT INTO q3panel_banned_ips (name, ip_address, server_id) VALUES (?, ?, 2)";
        $params = array($name, $ip);
        $dat = $sql->query($query, $params);
        //echo Constants::$SERVER_ACTIONS['Q3_RCON_COMMAND'] . "ban $id ID-" . $dat['last_insert_id'] . "-VPN_Autoban";
        $output = $srv->sendQ3Command(Constants::$SERVER_ACTIONS['Q3_RCON_COMMAND'] . "ban $id ID-" . $dat['last_insert_id'] . "-VPN_Autoban", true, true);
    } else {
        $query = "INSERT INTO q3panel_allowed_ips (ip_address, blocklevel, name) VALUES (?, ?, ?)";
        $params = array($ip, $jsonarr['block'], $name);
        $sql->query($query, $params);

function stripQ3Colors($input) {
    return preg_replace("/(\^.)/", "", $input);

So if one would want to add this without the usage of q3panel, you'll need to rewrite some portions of it, you need to create a connection handle to your server with the RCON password and change some of the variables and functions. Or you can also use some parts of the q3panel code from here: https://github.com/JannoEsko/q3panel

Hope it's interesting for you and that you'll find some benefit from it.



3D# General
Staff member
That's pretty neat!
There isn't a free plan for the use of this API is there? For testing purposes
There is, free api just has its limits. If I recall correctly, the only limit is x amount of IP’s in 5 minutes or something like that.