CRON job for failover on pfSense



  • Hi,

    I wrote a small script for webserver failover that I run as a CRON job on the pfSense system. The script checks if a remote website is running and in case it is down, a local backup server is started and DNS settings are adjusted. When the remote server is running again, the local server is shutdown (after a timeout based on the TTL of the remote server in order to allow DNS propagation).

    My question is: (a) am I not overloading the pfSense system (Netgate SG-3100) by running this script in CRON, (b) does this have a negative effect on the security of the gateway?

    While the script works fine, I have set CRON to only run it every 15 minutes (higher frequency would be nice though).

    For anybody interested in the script, I include it in the post.

    regards.

    <?php
    
    // requirements: an account on DNS server to change A records, a backup server with SSH access, keyless login to backup server, the correct firewall and proxy settings for providing access to the backup server
    
    //
    // params
    //
    $hosts    = array('mydomain.com','www.mydomain.com','my.mydomain.com');	// the URL's to be redirected
    $svr_ip   = '192.168.1.10';												// the IP address of the backup server
    $svr_mac  = 'e8:2c:b3:fc:3d:1b';										// the Mac address of the backup server
    $port     = 22; 														// the ssh port of the backup server
    $rsa_dir  = '/root/.ssh';												// the key location on pfSense system
    $ssh_user = 'user2';													// the user account on backup server
    $rem_svr  = 'remote.mydomain.com';										// the URL to monitor
    $rem_port = 443;														// the service port on the remote server
    $dns_usr  = 'user1';													// the user account on DNS server
    $dns_pwd  = 'mypasswd';													// the password on DNS server
    $dns_svr  = 'https://eurodyndns.org/update/';							// the URL of the DNS update service
    $dns_ttl  = 15;															// the TTL of the domain
    $status   = '/root/failover.on';										// the file indicating the status
    $ip_svr   = 'http://checkip.dyndns.com/';								// URL to obtain external IP address
    $reboot   = '/sbin/shutdown';											// shutdown command on backup server
    //
    // check is site in online
    //
    if(!$socket =@ fsockopen($rem_svr, $rem_port, $errno, $errstr, 30)) {
    	//
    	// check if site is already in offline mode
    	//
    	if (!file_exists($status)) {
    		//
    		// wake backup server
    		//
    		$wake = new WakeOnLan;
    		$wake->WakeUp($svr_mac,$svr_ip);
    		//
    		// update DNS server
    		//
            $ext = file_get_contents($ip_svr);
            preg_match('/\b(?:\d{1,3}\.){3}\d{1,3}\b/', $ext, $m);
            $addr = $m[0];
    		$up = new UpdateDNS;
    		foreach ($hosts as $host) {
    			$up->update($addr,$host,$dns_usr,$dns_pwd,$dns_svr);
    		}
    		date_default_timezone_set("Europe/Brussels");
    		file_put_contents($status,time());
    	}
    } else {
    	if (file_exists($status)) {
    		//
    		// site recovered: get address of the site (in case it has changed)
    		//
                    $addr = gethostbyname($rem_svr);
                    //
                    // update DNS server
                    //
                    $up = new UpdateDNS;
                    foreach ($hosts as $host) {
                            $up->update($addr,$host,$dns_usr,$dns_pwd, $dns_svr);
                    }
    		//
    		// remove failover flag
    		//
    		unlink($status);
    		//
    		// shutdown backup server
    		//
    		$conn = ssh2_connect($svr_ip, $port, array('hostkey'=>'ssh-rsa'));
    		if (ssh2_auth_pubkey_file($conn, $ssh_user, $rsa_dir.'/id_rsa.pub', $rsa_dir.'/id_rsa')) {
    		        $res = ssh2_exec($conn, 'sudo '.$reboot."+".(2*$dns_ttl));
    		}
    	}
    }
    class UpdateDNS {
    	public static function update($addr,$fqdn,$user,$pwd,$dns_svr) {
    		$ch = curl_init();
    		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 
    		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);   
            curl_setopt($ch, CURLOPT_TIMEOUT, 120); 
    		curl_setopt($ch, CURLOPT_USERPWD, $user.':'.$pwd);
            curl_setopt($ch, CURLOPT_URL, $dns_svr . '?hostname=' . $fqdn . '&myip=' . $addr);
    		$res = curl_exec($ch);
    		curl_close($ch);
    	}
    }
    class WakeOnLAN
    {
        public static function wakeUp($macAddressHexadecimal, $broadcastAddress)
        {
            $macAddressHexadecimal = str_replace(':', '', $macAddressHexadecimal);
            if (!ctype_xdigit($macAddressHexadecimal)) {
                throw new \Exception('Mac address invalid, only 0-9 and a-f are allowed');
            }
            $macAddressBinary = pack('H12', $macAddressHexadecimal);
            $magicPacket = str_repeat(chr(0xff), 6).str_repeat($macAddressBinary, 16);
            if (!$fp = fsockopen('udp://' . $broadcastAddress, 7, $errno, $errstr, 2)) {
                throw new \Exception("Cannot open UDP socket: {$errstr}", $errno);
            }
            fputs($fp, $magicPacket);
            fclose($fp);
        }
    }
    
    ?>
    

Log in to reply