WHMCS Technical Analyst WHMCS JoshQ Posted March 1 WHMCS Technical Analyst Share Posted March 1 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! 1 Quote Link to comment Share on other sites More sharing options...
mfoland Posted March 19 Share Posted March 19 Is there a way to do a whitelist for all IP's? I use the API since I sell software, and things I've developed make use of the WHMCS API. 0 Quote Link to comment Share on other sites More sharing options...
RadWebHosting Posted August 5 Share Posted August 5 On 3/18/2024 at 9:22 PM, mfoland said: Is there a way to do a whitelist for all IP's? I use the API since I sell software, and things I've developed make use of the WHMCS API. Technically, the following CIDR should whitelist all IPv4 addresses: 0.0.0.0/0 0 Quote Link to comment Share on other sites More sharing options...
WHMCS Technical Analyst WHMCS JoshQ Posted August 7 Author WHMCS Technical Analyst Share Posted August 7 On 05/08/2024 at 20:40, RadWebHosting said: Technically, the following CIDR should whitelist all IPv4 addresses: 0.0.0.0/0 Yes, I expect that this would work. However, I would strongly recommend against permitting API access from all API addresses. We put the API behind a whitelist for a very good reason. 0 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.