Automating SMTP Port Management Across VPS Nodes: A Step-by-Step Guide for Virtualizor based KVM VPS Nodes

If you manage multiple VPS nodes and offer SMTP services selectively to clients, automating the management of IP sets can save significant effort. In this guide, we’ll walk through how we automated the synchronization of SMTP-enabled IPs across over 100 VPS nodes.

This tutorial has been tested and is fully operational on Virtualizor-based KVM VPS nodes. The script is configured to run at 1-hour intervals by default, but you can adjust the interval depending on your requirements and available resources. It can be set up on a separate server, on the same server as WHMCS, or another VPS. If using the WHMCS server, ensure it is properly secured, as this script has access to all your servers.


Prerequisites

  1. Python 3.x installed on your system.
  2. Required Python libraries:
   pip install paramiko pandas
  1. WHMCS with VPS product configurations.
  2. SSH access to all VPS nodes and the WHMCS server.
  3. ipset installed and configured on each VPS node.
  4. Proper iptables rules set up on all VPS nodes (detailed below).

Required iptables and ipset Configuration on VPS Nodes

To manage SMTP access effectively, you need the following iptables and ipset rules configured on all VPS nodes. These rules must also persist across reboots:

modprobe br_netfilter
ipset create allowed_ips hash:ip
iptables -F
iptables -P FORWARD DROP
iptables -I FORWARD -m set --match-set allowed_ips src -o viifbr0 -p tcp --dport 25 -j ACCEPT
iptables -I FORWARD -m set --match-set allowed_ips dst -o viifbr0 -p tcp --dport 25 -j ACCEPT

iptables -A FORWARD -o viifbr0 -p tcp --dport 25 -j REJECT
iptables -A FORWARD -o viifbr0 -j ACCEPT
service iptables save 

These rules ensure that SMTP traffic is blocked by default unless explicitly allowed via ipset. Ensure the rules are applied on every reboot of the VPS nodes.


Overview of the Solution

  1. Fetch VPS Configuration from WHMCS: Retrieve a JSON file listing VPS configurations, including SMTP-enabled status and associated IPs.
  2. Process Data: Parse the JSON file to extract primary and additional IPs for SMTP-enabled VPSs.
  3. Sync IP Sets Across Nodes: Use ipset to update allowed IPs for SMTP on each node. This includes adding or removing IPs as needed.
  4. Parallel Execution: Speed up the process by handling multiple nodes concurrently with Python threading.

Implementation

1. Create the Excel File for Node Information

The Python script uses an Excel file to identify the SSH IPs and ports of all VPS nodes. Create an Excel file in the following format:

IP AddressSSH Port
192.168.1.10022
192.168.1.1012222

Save this file as securecrt_servers.xlsx and ensure it is accessible to the script.

2. Fetch VPS Data from WHMCS

Add a hook in WHMCS to export VPS data:

File: /path/to/whmcs/includes/hooks/export_vps_data.php

<?php

use Illuminate\Database\Capsule\Manager as Capsule;

add_hook('AfterCronJob', 100, function($vars) {
    $logFile = __DIR__ . '/export_hook_debug.log';
    $filePath = __DIR__ . '/vps_data.json';

    try {
        $vpsData = Capsule::table('tblhosting')
            ->join('tblproducts', 'tblhosting.packageid', '=', 'tblproducts.id')
            ->join('tblclients', 'tblhosting.userid', '=', 'tblclients.id')
            ->leftJoin('tblhostingconfigoptions', 'tblhosting.id', '=', 'tblhostingconfigoptions.relid')
            ->leftJoin('tblproductconfigoptions', 'tblhostingconfigoptions.configid', '=', 'tblproductconfigoptions.id')
            ->select(
                'tblclients.firstname',
                'tblclients.lastname',
                'tblhosting.dedicatedip',
                'tblhosting.assignedips',
                'tblhosting.domain',
                'tblproducts.name as productname',
                'tblproductconfigoptions.optionname',
                'tblhostingconfigoptions.optionid'
            )
            ->where('tblproducts.type', 'server')
            ->where('tblhosting.domainstatus', 'Active')
            ->get();

        $formattedData = [];
        foreach ($vpsData as $vps) {
            $smtp_enabled = false;
            if (stripos($vps->optionname ?? '', 'SMTP Access') !== false && $vps->optionid > 0) {
                $smtp_enabled = true;
            }

            $formattedData[] = [
                'client_name' => $vps->firstname . ' ' . $vps->lastname,
                'primary_ip' => $vps->dedicatedip,
                'additional_ips' => $vps->assignedips,
                'domain' => $vps->domain,
                'product_name' => $vps->productname,
                'smtp_enabled' => $smtp_enabled,
            ];
        }

        file_put_contents($filePath, json_encode($formattedData, JSON_PRETTY_PRINT));
    } catch (Exception $e) {
        file_put_contents($logFile, "Error: " . $e->getMessage() . PHP_EOL, FILE_APPEND);
    }
});

3. Configure SMTP Access Using WHMCS Configurable Options

To enable or disable SMTP for a VPS:

  1. Set Up a Configurable Option:
  • Go to WHMCS Admin > Products/Services > Configurable Options.
  • Create an option named SMTP Access with values such as Enabled and Disabled.
  1. Client Self-Management (Optional):
  • If you want clients to manage this option while ordering or upgrading, associate the configurable option with the product.
  1. Manual Control:
  • To keep SMTP access manual, hide the configurable option from clients and enable or disable it directly in the admin panel.

Note: Changes to SMTP access will take effect within the interval configured for the sync script (default: 1 hour).

4. Automate Syncing with Python

File: /path/to/script/smtp_sync.py

import requests
import subprocess
import paramiko
import ipaddress
import os
import pandas as pd
import re
import json
from concurrent.futures import ThreadPoolExecutor

DEBUG = True

NODES_FILE_PATH = '/path/to/securecrt_servers.xlsx'
nodes_df = pd.read_excel(NODES_FILE_PATH)
NODES = [
    {"host": row["IP Address"], "port": row["SSH Port"]}
    for _, row in nodes_df.iterrows()
]

IPSET_NAME = "allowed_ips"
ERROR_LOG_FILE = "node_errors.log"
WHMCS_SERVER = {
    "host": "whmcs-server-ip",
    "user": "your-whmcs-user",
    "port": 22,
    "key_path": os.path.expanduser("~/.ssh/id_rsa")
}
REMOTE_VPS_FILE = "/path/to/whmcs/hooks/vps_data.json"
LOCAL_VPS_FILE = "/tmp/vps_data.json"

# Define functions for fetching, processing, and syncing IPs
# See the complete script in the provided implementation.

Cron Job Setup

Run the Python script every hour by adding it to your crontab:

crontab -e

Add the following line:

0 * * * * /usr/bin/python3 /path/to/script/smtp_sync.py >> /var/log/smtp_sync.log 2>&1

Benefits of the Solution

  1. Automated Management: No manual updates to IP sets are required.
  2. Scalability: Handles hundreds of nodes efficiently using multithreading.
  3. Reliability: Synchronization ensures consistent SMTP access control across all nodes.