Jump to content

Whitelisting IP ranges for API access


Recommended Posts

  • WHMCS Technical Analyst

For businesses that heavily utilise WHMCS's API, you may be sending API requests from many different IP addresses or IP ranges, all of which need to be whitelisted at System Settings > General Settings > Security (tab) > API IP Access Restriction in the Admin Area.

Each individual IP address must be added to the allow list (it is not possible to list IP ranges in CIDR notation at this time). The process of adding a range of IP addresses can be sped up by using a PHP script to add them in bulk.

Before we begin, WHMCS support does not recommended directly interacting with the database where it can be avoided. We would therefore only recommend following this guide if you have an existing understanding of both PHP and database administration, and have taken a full database backup. WHMCS will not be held responsible for any data loss that might occur if you do not heed this advice.

 

How are API Allowed IPs stored?

The list of IPs allowed to interact with the API are stored as a serialized array in tblconfiguration.value where tblconfiguration.setting = "APIAllowedIPs".

A serialized array, in this particular context,  is a way for us to store an array as a string in the database.

 

Handling existing data in WHMCS's database

To make things easy, let's create a function that takes in the current serialized array, which you can retrieve from the database, a new IP and a note, and then returns the new serialized array.

Here it is, with everything explained in inline comments:

function addNewIP($currentValue, $ip, $note) {
    // Take the $currentValue, and unserialize it so we can interact with it directly.
    $data = unserialize($currentValue);
 
    // Define our new entry, based on the $ip and $note provided in the function call.
    $newEntry = array(
        'ip' => $ip,
        'note' => $note
    );
 
    // Add the new entry to the existing array.
    $data[] = $newEntry;
 
    // Re-serialize the array.
    $newValue = serialize($data);
 
    // Return to the serialized version of the new array.
    return $newValue;
}

 

Iterating through IP ranges

We now need to iterate through all IP address in a given range and add them to our array.

Let's create another function to do this:

function addNewIPRange($currentValue, $startIP, $endIP, $note) {
    // Convert the start and end IP addresses to long integers so we can iterate through them
    $startIP = ip2long($startIP);
    $endIP = ip2long($endIP);
 
    // Initialize $newValue
    $newValue = $currentValue;
 
    // Iterate through the IP range
    for ($ip = $startIP; $ip <= $endIP; $ip++) {
        // Convert the current IP back to string format
        $newIP = long2ip($ip);
 
        // Call the addNewIP function for each IP in the range
        $newValue = addNewIP($newValue, $newIP, $note);
    }
 
    // Return the modified value
    return $newValue;
}

Note that we'll still need to provide the current value from the database, and this will be updated each time an IP in the range is added.

 

Testing it out

First of all, we need to retrieve the current serialized array from the database. This can be done programmatically using Capsule, but in this case we'll just retrieve it manually:

SELECT value FROM tblconfiguration WHERE setting = 'APIAllowedIPs'

Then we can call addNewIPRange to add an IP range, or just addNewIP if we only want to add a single address.

Of course, when you're using this you wouldn't need to add all of the extra echo lines or comments - these are just included to help demonstrate how this is working.

// Store current APIAllowedIPs value in variable
$currentValue = 'a:5:{i:0;a:2:{s:2:"ip";s:0:"";s:4:"note";s:0:"";}i:1;a:2:{s:2:"ip";s:12:"10.3.137.243";s:4:"note";s:0:"";}i:2;a:2:{s:2:"ip";s:7:"8.8.8.8";s:4:"note";s:7:"Testing";}i:3;a:2:{s:2:"ip";s:7:"1.1.1.1";s:4:"note";s:7:"Testing";}i:4;a:2:{s:2:"ip";s:11:"12.13.14.15";s:4:"note";s:7:"Testing";}}';
 
// Let's see our $currentValue in a more readable format so that we can compare it to our new version
echo "<h1>Starting value</h1>";
echo "<pre>"; print_r(unserialize($currentValue)); "</pre>";
echo "<br/><hr/>";
 
// To add a new IP range, call addNewIPRange, and pass in the $currentValue, IP range and a note:
$testRange = addNewIPRange($currentValue, '10.20.30.40', '10.20.30.60', 'Test IP Range');
 
echo "<h1>With our added IP range</h1>";
echo "<h2>Serialised version:</h2>";
echo $testRange;
echo "<h2>Unserialised version:</h2>";
echo '<pre>'; print_r(unserialize($testRange)); '</pre>';
echo "<br/><hr/>";
 
// To add a single IP, call addNewIP, and pass in our new value, address and a note:
$testSingle = addNewIP($testRange, '192.168.1.1', 'Single IP');
 
echo "<h1>And with one more address</h1>";
echo "<h2>Serialised version:</h2>";
echo $testSingle;
echo "<h2>Unserialised version:</h2>";
echo '<pre>'; print_r(unserialize($testSingle)); '</pre>';
echo "<br/><hr/>";

You can then add the new data to the database manually, or using Capsule. Doing the latter is beyond the scope of this article.

 

The final script

Let's finish by tidying up the scirpt a bit, and showing an example.

Let's say we wanted to whitelist the following:

  • 192.168.45.23 - management VPN
  • 192.168.45.24 - development VPN
  • Range from 10.20.0.126 to 10.20.10.159 - backend servers

Here's what your script would look like:

<?php
 
function addNewIP($currentValue, $ip, $note) {
    $data = unserialize($currentValue);
    $newEntry = array(
        'ip' => $ip,
        'note' => $note
    );
    $data[] = $newEntry;
    $newValue = serialize($data);
    return $newValue;
}
 
function addNewIPRange($currentValue, $startIP, $endIP, $note) {
    $startIP = ip2long($startIP);
    $endIP = ip2long($endIP);
    $newValue = $currentValue;
    for ($ip = $startIP; $ip <= $endIP; $ip++) {
        $newIP = long2ip($ip);
        $newValue = addNewIP($newValue, $newIP, $note);
    }
    return $newValue;
}
 
$currentValue = 'a:5:{i:0;a:2:{s:2:"ip";s:0:"";s:4:"note";s:0:"";}i:1;a:2:{s:2:"ip";s:12:"10.3.137.243";s:4:"note";s:0:"";}i:2;a:2:{s:2:"ip";s:7:"8.8.8.8";s:4:"note";s:7:"Testing";}i:3;a:2:{s:2:"ip";s:7:"1.1.1.1";s:4:"note";s:7:"Testing";}i:4;a:2:{s:2:"ip";s:11:"12.13.14.15";s:4:"note";s:7:"Testing";}}';
 
$manVpnIP = addNewIP($currentValue, "192.168.45.23", "Management VPN");
$devVpnIP = addNewIP($manVpnIP, "192.168.45.24", "Development VPN");
$backendServers = addNewIPRange($devVpnIP, "10.20.0.126", "10.20.0.159", "Backend Server");
 
echo $backendServers;

The output of this script would be the new serialized array, and you can set this directly in the database!

Again - please make sure to take a backup before manually interacting with WHMCS's database.

UPDATE tblconfiguration SET value = '<add your serialized array here>' WHERE setting = 'APIAllowedIPs';

Important: You must use single quotes and not double quotes, else the command's format will not be valid (since double quotes are used in the serialized array itself).

Once you have run this, login to the WHMCS admin area and navigate to System Settings > General Settings > Security (tab). You should now see a list of all your IPs listed in the API IP Access Restriction setting.

 

Further considerations

After adding so many IP addresses, you may find that you are no longer able to add any more addresses via the Admin Area. This will be because your PHP max_input_vars is being exhausted. It is not a bug with WHMCS, nor is it something that we can mitigate - this is how PHP is configured by default.

https://www.php.net/manual/en/info.configuration.php#ini.max-input-vars

To rectify this, please increase the max_input_vars value in your PHP configuration. Should you need assistance with doing this, please contact your hosting provider or system administrator.

 

Customising/extending this script

Feel free to customise this script to you heart's content, whether you want to change how it behaves or extend its functionality.

However, please keep in mind that our Technical Support team won't be able to help you troubleshoot any problems that you may encounter should you make any changes. Use at your own risk.

 

Suggestions for other "Tips & Tricks' articles

If you'd like to see an example of how we can customise WHMCS in other ways, please do give us your suggestions!

Link to comment
Share on other sites

  • 3 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use & Guidelines and understand your posts will initially be pre-moderated