Adding IPv6 subnet protection to Virtualizor

Edit: As of the latest version of Virtualizor, this tutorial is no longer needed!

For our VPS service, we choose to use Virtualizor instead of SolusVM due to its end user interface, which is easier and looks more modern than Solus. We noticed Virtualizor ebtables rules do not include anything to protect IPv6 subnets, so we created our own hook scripts so users cannot change their assigned IPv6 to anything they want.

How we did it? We first created three simple helper scripts:

#This script matches the mac with the IPv6 subnet
ip6=$(/usr/local/sbin/ $3)
tstp=$(date +"%s")
cmd="-A vm${vmname} -p IPv6 -s ${vmmac} --ip6-src ${ip6}${mask} -j ACCEPT"
sleep 5
/sbin/ebtables -t filter ${cmd}
echo "${tstp} - Adding rule \"${cmd}\" retcode $?" >> /var/log/ebtables_ipv6.log
#This script allows the subnet to be forwarded
tstp=$(date +"%s")
cmd="-A FORWARD -p IPv6 -i viif${vmname} -j vm${vmname}"
sleep 5
if [ $(/sbin/ebtables-save | grep -Fxc -- "${cmd}") -gt 0 ]
  echo "${tstp} - IPv6 FORWARD rule for \"${vmname}\" already in place" >> /var/log/ebtables_ipv6.log
  /sbin/ebtables -t filter ${cmd}
  echo "${tstp} - Adding rule \"${cmd}\" retcode $?" >> /var/log/ebtables_ipv6.log

#!/usr/bin/env python3
#This tiny python3 script shortens IPv6 addresses!
import sys
import ipaddress

if __name__ == "__main__":
  if(len(sys.argv) == 1):
    ips = [item.strip() for item in sys.stdin.readlines()]
    for ipv6 in ips:

  elif (len(sys.argv) == 2):

And finally, the hook to after_startvps.php in Virtualizor!

function __after_startvps($vps){

    // Write your code here. e.g. You can add additional parameters in the config file of the the VPS.

    // List the IPs
    $res = makequery("SELECT i.*, ip.* FROM ips i
                      LEFT JOIN ippool ip ON (ip.ippid = i.ippid) 
                      WHERE i.vpsid = :vid
                      ORDER BY `primary` DESC", array(':vid' => $vps['vpsid']));   
    for($i=0; $i < vsql_num_rows($res); $i++){
        $ips[$i] = vsql_fetch_assoc($res);
    $path = '/etc/libvirt/qemu/'.$vps['vps_name'].'.xml';

    $doc = new DOMDocument;
    $root = $doc->documentElement;
    $mac0tag = $root->getElementsByTagName('mac')->item(0);
    $mac_addr = $mac0tag->getAttribute('address');

    $vps_name = $vps['vps_name'];
    //$mac_addr = $vps['mac'];
    $ipv6 = array();
    $ipv6_m = array();
    //file_put_contents('/tmp/ids.txt', $vps_name." ".$mac_addr);
    for($i=0;$i < count($ips); $i++){
      if($ips[$i]['ipv6'] == '1' ){
         $ipv6[] = $ips[$i]['ip'];
         $ipv6_m[] = $ips[$i]['ipr_netmask'];
    $out1 = shell_exec('/usr/local/sbin/ '.$vps_name);
    for($i=0;$i < count($ipv6); $i++){
        $ipv6_m[$i] = '/'.$ipv6_m[$i];
      $out2[$i] = shell_exec('/usr/local/sbin/ '.$vps_name.' '.$mac_addr.' '.$ipv6[$i].' '.$ipv6_m[$i]);    

Now you should be able to make Virtualizor VPS unable to use any other IPv6 subnet, besides the assigned one. You can check if it is adding rules by using “ebtables-save” as root.

Beware these scripts were only tested when assigning /64 subnets! Good luck!

Leave a Reply

Leave a Reply

Your email address will not be published. Required fields are marked *