Bladeren bron

Merge branch 'test3' of andre/pfsense into master

andre 5 jaren geleden
bovenliggende
commit
96bdaa6d9c
2 gewijzigde bestanden met toevoegingen van 204 en 70 verwijderingen
  1. 24 0
      cloudinit.sh
  2. 180 70
      proxmox_cloud-init.php

+ 24 - 0
cloudinit.sh

@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# PROVIDE: cloudinit
+# REQUIRE: FILESYSTEMS netif syslogd
+# BEFORE:  LOGIN
+
+. /etc/rc.subr
+
+name="cloudinit"
+
+start_cmd="${name}_start"
+stop_cmd=":"
+
+load_rc_config $name
+: ${cloudinit_enable:=yes}
+: ${cloudinit_msg="Starting CloudInit."}
+
+cloudinit_start()
+{
+	/usr/local/bin/php /usr/local/sbin/proxmox_cloud-init.php
+}
+
+run_rc_command "$1"
+

+ 180 - 70
proxmox_cloud-init.php

@@ -1,58 +1,153 @@
 <?php
 <?php
 /**
 /**
- * cloud-init script for pfSense on Proxmox VE
+ * cloudinit script for pfSense on Proxmox VE
  * 
  * 
  * The script does a pfSense configuration in that manner:
  * The script does a pfSense configuration in that manner:
- * 1. looks for an available cloud-init drive and mount it to /etc/cloud-init
- * 2. compares the cloud-init files on the cloud-init drive by a local copy placed in /etc/cloud-init
+ * 1. look for an available cloudinit drive and mount it to /mnt/cloud
+ * 2. compares the cloudinit files on the cloudinit drive by a local copy placed in /etc/cloud
  *    and ends without changing anything if the files on both location are similar
  *    and ends without changing anything if the files on both location are similar
- * 3. parses the cloud-init YAML files and prepare and set the given values to pfSense config array
+ * 3. parses the cloudinit YAML files and prepare and set the given values to pfSense config array
  * 4. write the pfSense configuration and reboot the instance
  * 4. write the pfSense configuration and reboot the instance
+ * 
  * HowTo install:
  * HowTo install:
  * 1. get a copy of the YAML parse from https://github.com/mustangostang/spyc/ and place it to /usr/local/sbin
  * 1. get a copy of the YAML parse from https://github.com/mustangostang/spyc/ and place it to /usr/local/sbin
- * 2. place a copy of proxmox_cloud-init.php also to /usr/local/sbin
- * HowTo use:
- * 1. attach a cloud-init drive to the pfSense VM
- * 2. create a startupscript
+ * 2. place a copy of proxmox_cloudinit.php also to /usr/local/sbin
+ * 3. place a startup script in /usr/local/etc/rc.d/
+ * 
+ * Example startup script
+ * ----------------------------------------------------------------------------
+ * #!/bin/sh
+ * # PROVIDE: cloudinit
+ * # REQUIRE: FILESYSTEMS netif syslogd
+ * # BEFORE:  LOGIN
+ * 
+ * . /etc/rc.subr
+ * 
+ * name="cloudinit"
+ * 
+ * start_cmd="${name}_start"
+ * stop_cmd=":"
  * 
  * 
- * @version 0.9
+ * load_rc_config $name
+ * : ${cloudinit_enable:=yes}
+ * : ${cloudinit_msg="Starting CloudInit."}
+ * 
+ * cloudinit_start()
+ * {
+ * 	/usr/local/bin/php /usr/local/sbin/proxmox_cloud-init.php
+ * }
+ * 
+ * run_rc_command "$1"
+ * ----------------------------------------------------------------------------
+ * 
+ * HowTo use:
+ * 1. attach a cloudinit cdrom to the pfSense VM an boot
+ * 2. boot twice within 5 minutes to force a change run (don't count the automated reboots)
+ * 3. boot thrice within 5 minutes between each reboot to force a restore to factory defaults (don't count the automated reboots)
+ *  
+ * @version 0.9.1
  * @author Andre Genrich andre.genrich@thurdata.ch
  * @author Andre Genrich andre.genrich@thurdata.ch
  */
  */
 require_once('Spyc.php');                                                      // yaml parser
 require_once('Spyc.php');                                                      // yaml parser
 require_once('config.inc');                                                    // pfSense configuration structures
 require_once('config.inc');                                                    // pfSense configuration structures
 require_once('system.inc');                                                    // pfSense system utilities
 require_once('system.inc');                                                    // pfSense system utilities
-$cloudInitFiles = array('meta-data', 'network-config', 'user-data');           // provided by Proxmox
-$cloudInitLocalPath = '/etc/cloud-init';
-$cloudInitMountPoint = '/mnt/cloud-init';
+static $cloudInitFiles = array('meta-data', 'network-config', 'user-data');    // provided by Proxmox
+// tuneables
+static $cloudInitLocalPath = '/etc/cloud';                                     // place for $cloudInitControlFile, $cloudInitResetRequest and a local copy of $cloudInitFiles
+static $cloudInitMountPoint = '/mnt/cloud';                                    // mountpoint for cloudinit drive
+static $cloudInitControl = 'lastrun';                                          // file contains the timestamp of the last run or "init" after a change run
+static $cloudInitResetControl = 'emergency';                                   // file contains the timestamp of the last emergency run
+static $unstableRunTimeout = 300;                                              // after $unstableRunTimeout the script assumes the system is stable
 /**
 /**
- * compares the cloud-init files
+ * calcCIDR calculates a netmask in CIDR notation from dotted decimal notation
  * 
  * 
- * conpares the cloud-init drive by a local copy and
- * update the local copy in case of changes
- * or if the local copy does not exist
+ * @param string $mac netmask in dotted decimal notation
+ * @return int $cidr netmask in CIDR notation
+ */
+function calcCIDR( $mac) {
+	$cidr = 32 - log((ip2long($mac) ^ ip2long('255.255.255.255')) + 1 ,2);
+	return $cidr;
+}
+/**
+ * createDir creates necessary directories
  * 
  * 
- * @param array $cloudInitFiles an array of filenames created by Proxmox
- * @param string $cloudInitLocalPath the local path to place the copy of cloud-init files
- * @param string $cloudInitMountPoint the location where the cloud-init image is mounted
- * @return bool true in case of updates, false in case of all is up to date 
+ * @param string $path is the full path of the directory
  */
  */
-function updateCloudInitFiles( $cloudInitFiles, $cloudInitLocalPath, $cloudInitMountPoint) {
-	$cloudInitFileDiff = false;
-	// create /etc/cloud-init if not exist
-	if (!is_dir("$cloudInitLocalPath")) {
-		if (!mkdir ( "$cloudInitLocalPath", 0770, TRUE)) {
+function createDir( $path) {
+	if (!is_dir("$path")) {
+		if (!mkdir ( "$path", 0770, TRUE)) {
 			$sys_err= error_get_last();
 			$sys_err= error_get_last();
-			echo "cloud-init failed: $sys_err[type] $sys_err[message]\n";
-			syslog(LOG_ERR,"cloud-init failed: $sys_err[type] $sys_err[message]");
+			syslog(LOG_ERR,"cloudinit: failed to create $path, error $sys_err[type] $sys_err[message]");
 			exit(1);
 			exit(1);
 		}
 		}
 	}
 	}
+}
+/**
+ * restoreConfiguration removes all local cloudinit files and starts a reset to factory defaults
+ * 
+ */
+function restoreConfiguration() {
+	global $cloudInitLocalPath, $cloudInitFiles, $cloudInitControl, $cloudInitResetControl;
+	unlink("$cloudInitLocalPath/$cloudInitControl");
+	unlink("$cloudInitLocalPath/$cloudInitResetControl");
+	foreach ( $cloudInitFiles as $cloudInitFile) {
+		unlink("$cloudInitLocalPath/$cloudInitFile");
+	}
+	reset_factory_defaults();
+	system_reboot_sync();
+}
+/**
+ * controlEmergencyRun check and update run control files
+ * 
+ * $cloudInitControl contains "init" after a change run -> controlEmergencyRun outs the ciúrrent timestamp into $cloudInitControl
+ * 
+ * @return int 0 to trigger a skip run
+ * @return int 1 to trigger an emergency run (set again all cloudinit settings)
+ * @return int 3 to trigger a reset to factory defaults run (reset all cloudinit & pfSense changes)
+ */
+function controlEmergencyRun() {
+	global $cloudInitLocalPath, $cloudInitControl, $cloudInitResetControl;
+	if ((file_get_contents( "$cloudInitLocalPath/$cloudInitControl")) == "init") {
+		file_put_contents( "$cloudInitLocalPath/$cloudInitControl", time());
+		return 0;
+	}
+	if ((file_get_contents( "$cloudInitLocalPath/$cloudInitControl") + 300) > time()) {
+		if (file_exists( "$cloudInitLocalPath/$cloudInitResetControl")) {
+			if ((file_get_contents( "$cloudInitLocalPath/$cloudInitResetControl") + 300) > time()) {
+				unlink( "$cloudInitLocalPath/$cloudInitResetControl");
+				return 2;
+			} else { 
+				unlink( "$cloudInitLocalPath/$cloudInitResetControl");
+				return 1;
+			}
+		} else {
+			file_put_contents( "$cloudInitLocalPath/$cloudInitResetControl", time());		
+			return 1;
+		}
+	} else {
+		unlink( "$cloudInitLocalPath/$cloudInitResetControl");
+		file_put_contents( "$cloudInitLocalPath/$cloudInitControl", time());
+		return 0;
+	}
+}
+/**
+ * updateCloudInitFiles compares the cloudinit files
+ * 
+ * conpares the cloudinit drive by a local copy and
+ * update the local copy in case of changes
+ * or if the local copy does not exist
+ * 
+ * @return bool true in case of updates, false in case of all is up to date 
+ */
+function updateCloudInitFiles() {
+	global $cloudInitMountPoint, $cloudInitLocalPath, $cloudInitFiles;
+	$cloudInitFileDiff = false;
 	// check for updated config files and update the local copy in case of differs
 	// check for updated config files and update the local copy in case of differs
 	foreach ( $cloudInitFiles as $cloudInitFile ) {
 	foreach ( $cloudInitFiles as $cloudInitFile ) {
 		if (!((sha1_file("$cloudInitLocalPath/$cloudInitFile")) == (sha1_file("$cloudInitMountPoint/$cloudInitFile")))) {
 		if (!((sha1_file("$cloudInitLocalPath/$cloudInitFile")) == (sha1_file("$cloudInitMountPoint/$cloudInitFile")))) {
 			if (!copy("$cloudInitMountPoint/$cloudInitFile", "$cloudInitLocalPath/$cloudInitFile")) {
 			if (!copy("$cloudInitMountPoint/$cloudInitFile", "$cloudInitLocalPath/$cloudInitFile")) {
 				$sys_err = error_get_last();
 				$sys_err = error_get_last();
-				syslog(LOG_ERR,"cloud-init failed: $sys_err[type] $sys_err[message]");
+				syslog(LOG_ERR,"cloudinit failed: $sys_err[type] $sys_err[message]");
 				exit(1);
 				exit(1);
 			} else {
 			} else {
 				$cloudInitFileDiff = true;
 				$cloudInitFileDiff = true;
@@ -62,68 +157,60 @@ function updateCloudInitFiles( $cloudInitFiles, $cloudInitLocalPath, $cloudInitM
 	return $cloudInitFileDiff;
 	return $cloudInitFileDiff;
 }
 }
 /**
 /**
- * probes existence of all necessary cloud-init files
+ * checkCloudInitFiles probes existence of all necessary cloudinit files
  * 
  * 
- * @param array $cloudInitFiles an array of filenames created by Proxmox
- * @param string $cloudInitPath the location where the cloud-init files should be
- * @return bool true in case of all files are in place or false if something missing
+ * @return bool true in case of all files are in place or false if someone missing
  */
  */
-function checkCloudInitFiles( $cloudInitFiles, $cloudInitPath) {
+function checkCloudInitFiles() {
+	global $cloudInitMountPoint, $cloudInitFiles;
 	foreach($cloudInitFiles as $cloudInitFile) {
 	foreach($cloudInitFiles as $cloudInitFile) {
-		if (!file_exists("$cloudInitPath/$cloudInitFile")) {
+		if (!file_exists("$cloudInitMountPoint/$cloudInitFile")) {
 			return false;
 			return false;
 		}
 		}
 	}
 	}
 	return true;
 	return true;
 }
 }
 /**
 /**
- * search for a cloud-init drive
+ * checkCloudInitDevice search for a cloudinit drive
  * 
  * 
- * probes any attached cd device for existing cloud-init files
- * mounts the drive to /mnt/cloud-init and probes that all neccessary cloud-init files exist
+ * probes any attached cd device for existing cloudinit files
+ * mounts the drive to /mnt/cloudinit and probes that all neccessary cloudinit files exist
  * 
  * 
- * @param array $cloudInitFiles an array of filenames created by Proxmox
- * @param string $cloudInitMountPoint the mountpoint for cloud-init drive
- * @return bool true in case of success, fals in case of no cloud-init drive could found
+ * @return bool true in case of success, fals in case of no cloudinit drive could found
  */
  */
-function checkCloudInitDevice( $cloudInitFiles, $cloudInitMountPoint) {
+function checkCloudInitDevice() {
+	global $cloudInitMountPoint, $cloudInitFiles;
 	// get attached cd devices
 	// get attached cd devices
 	preg_match_all( "/.*cd[0-9] /", file_get_contents('/var/run/dmesg.boot'), $cdDeviceList);
 	preg_match_all( "/.*cd[0-9] /", file_get_contents('/var/run/dmesg.boot'), $cdDeviceList);
 	if (empty($cdDeviceList[0])) {
 	if (empty($cdDeviceList[0])) {
-		syslog(LOG_ERR,"cloud-init failed: No cloud-init drive found");
+		syslog(LOG_ERR,"cloudinit: no cloudinit drive found");
 		exit(1);
 		exit(1);
 	}
 	}
-	if(!is_dir($cloudInitMountPoint)) {
-		if(!mkdir($cloudInitMountPoint)) {
-			syslog(LOG_ERR,"cloud-init failed: Cloud not create mountpoint $cloudInitMountPoint");
-			exit(1);
-		}
-	}
-	// check cloud-init iso is mounted or try to mount the cloud-init medium
+	// check cloudinit iso is mounted or try to mount the cloudinit medium
 	foreach($cdDeviceList[0] as $cdDevice) {
 	foreach($cdDeviceList[0] as $cdDevice) {
-		// is a cd mounted on /mnt/cloud-init ?
+		// is a cd mounted on /mnt/cloudinit ?
 		$sys_err_msg = exec("df $cloudInitMountPoint | grep -q /dev/$cdDevice 2>&1", $sys_msg, $sys_err_no);
 		$sys_err_msg = exec("df $cloudInitMountPoint | grep -q /dev/$cdDevice 2>&1", $sys_msg, $sys_err_no);
 		if ($sys_err_no) { // not mounted
 		if ($sys_err_no) { // not mounted
 			// try to mount the cd
 			// try to mount the cd
 			$mount_err = exec("mount_cd9660 /dev/$cdDevice $cloudInitMountPoint", $sys_msg, $sys_err_no);
 			$mount_err = exec("mount_cd9660 /dev/$cdDevice $cloudInitMountPoint", $sys_msg, $sys_err_no);
 			if (!$sys_err_no) {
 			if (!$sys_err_no) {
-				if (checkCloudInitFiles( $cloudInitFiles, $cloudInitMountPoint)) {
-					syslog(LOG_INFO,"cloud-init: found cloud init drive on $cdDevice mounted at $cloudInitMountPoint/");
+				if (checkCloudInitFiles( $cloudInitMountPoint, $cloudInitFiles)) {
+					syslog(LOG_INFO,"cloudinit: found cloud init drive on $cdDevice mounted at $cloudInitMountPoint/");
 					return true;
 					return true;
 				} else {
 				} else {
 					$umount_err = exec("umount $cloudInitMountPoint", $sys_msg, $sys_err_no);
 					$umount_err = exec("umount $cloudInitMountPoint", $sys_msg, $sys_err_no);
 					if ($sys_err_no) {
 					if ($sys_err_no) {
-						syslog(LOG_ERR,"cloud-init: mounted a wrong device $cdDevice but not able to umount because $sys_msg");
+						syslog(LOG_ERR,"cloudinit: mounted a wrong device $cdDevice but not able to umount because $sys_msg");
 						return false;
 						return false;
 					}
 					}
 				}
 				}
 			}
 			}
 		} else { //already mounted (but not by us)
 		} else { //already mounted (but not by us)
-			if (checkCloudInitFiles( $cloudInitFiles, $cloudInitMountPoint)) {
-				syslog(LOG_INFO,"cloud-init: found cloud init drive on $cdDevice at $cloudInitMountPoint/");
+			if (checkCloudInitFiles( $cloudInitMountPoint, $cloudInitFiles)) {
+				syslog(LOG_INFO,"cloudinit: found cloud init drive on $cdDevice at $cloudInitMountPoint/");
 				return true;
 				return true;
 			} else {
 			} else {
-				syslog(LOG_ERR,"cloud-init: expected files on cloud init drive not found");
+				syslog(LOG_ERR,"cloudinit: expected files on cloud init drive not found");
 				return false;
 				return false;
 			}
 			}
 		}
 		}
@@ -131,7 +218,7 @@ function checkCloudInitDevice( $cloudInitFiles, $cloudInitMountPoint) {
 	return false;
 	return false;
 }
 }
 /**
 /**
- * does a case insensitive search for a network device by given hardware address
+ * searchIfDevice does a case insensitive search for a network device by given hardware address
  * 
  * 
  * @param string $mac hardware address
  * @param string $mac hardware address
  * @return string $if[1] device name or false if no device could be found
  * @return string $if[1] device name or false if no device could be found
@@ -146,15 +233,30 @@ function searchIfDevice( $mac) {
 	}
 	}
 	return false;
 	return false;
 }
 }
-// search and mount the cloud-init image or exit 1
-if (!checkCloudInitDevice( $cloudInitFiles, $cloudInitMountPoint)) {
-	syslog(LOG_ERR,"cloud-init: no cloud init drive available, skipping...\n");
+// create mountpoint for cloudinit drive if not exist
+createDir( $cloudInitMountPoint);
+// create local folder for config & control files
+createDir( $cloudInitLocalPath);
+// search and mount the cloudinit image or exit 1
+if (!checkCloudInitDevice()) {
+	syslog(LOG_ERR,"cloudinit: no cloud init drive available, skipping...\n");
 	exit(1);
 	exit(1);
 }
 }
-// update the local copy of cloud-init files if there are any changes or exit 0
-if (!(updateCloudInitFiles( $cloudInitFiles, $cloudInitLocalPath, $cloudInitMountPoint))) {
-	syslog(LOG_INFO,"cloud-init: cloud init files up to date, skipping...\n");
-	exit(0);
+// probe for special run modes
+if (!updateCloudInitFiles()) {
+	switch (controlEmergencyRun()) {
+		case 0:
+			syslog(LOG_INFO,"cloudinit: cloud init files up to date, skipping...\n");
+			exit(0);
+		break;
+		case 1:
+			syslog(LOG_INFO,"cloudinit: cloud init files up to date, but emergency run triggered...\n");
+		break;
+		case 2:
+			syslog(LOG_INFO,"cloudinit: reset run triggered, restore cloudinit default configuration!\n");
+			restoreConfiguration();
+		break;
+	}
 }
 }
 // parse cloud init configurations
 // parse cloud init configurations
 // $metaData = Spyc::YAMLLoad("$cloudInitLocalPath/$cloudInitFiles[0]");       // meta-data (actually not in use)
 // $metaData = Spyc::YAMLLoad("$cloudInitLocalPath/$cloudInitFiles[0]");       // meta-data (actually not in use)
@@ -174,27 +276,31 @@ if (reset($netData['config'][$ifLastNr]) == 'nameserver') {                    /
 // configure WAN interface
 // configure WAN interface
 $wanDevice = searchIfDevice( $netData['config'][0]['mac_address']);
 $wanDevice = searchIfDevice( $netData['config'][0]['mac_address']);
 if (!$wanDevice) {
 if (!$wanDevice) {
-	syslog(LOG_ERR,"cloud-init: no WAN device found");
+	syslog(LOG_ERR,"cloudinit: no WAN device found");
 	exit(1);
 	exit(1);
 } else {
 } else {
 	$config['interfaces']['wan']['if'] = $wanDevice;
 	$config['interfaces']['wan']['if'] = $wanDevice;
 }
 }
 if ($netData['config'][0][0]['type'] == 'static') {
 if ($netData['config'][0][0]['type'] == 'static') {
 	$config['interfaces']['wan']['ipaddr'] = $netData['config'][0][0]['address'];
 	$config['interfaces']['wan']['ipaddr'] = $netData['config'][0][0]['address'];
-	$config['interfaces']['wan']['subnet'] = 32 - log((ip2long($netData['config'][0][0]['netmask']) ^ ip2long('255.255.255.255')) + 1 ,2);
+	$config['interfaces']['wan']['subnet'] = calcCIDR( $netData['config'][0][0]['netmask']);
 	$config['interfaces']['wan']['gateway'] = $netData['config'][0][0]['gateway'];
 	$config['interfaces']['wan']['gateway'] = $netData['config'][0][0]['gateway'];
+} elseif ($netData['config'][0][0]['type'] == 'dhcp4') {
+	$config['interfaces']['wan']['ipaddr'] = 'dhcp';
+	unset( $config['interfaces']['wan']['subnet']);
+	unset( $config['interfaces']['wan']['gateway']);
 }
 }
 // configure primary LAN device
 // configure primary LAN device
 $lanDevice = searchIfDevice( $netData['config'][1]['mac_address']);
 $lanDevice = searchIfDevice( $netData['config'][1]['mac_address']);
 if (!$lanDevice) {
 if (!$lanDevice) {
-	syslog(LOG_ERR,"cloud-init: no LAN device found");
+	syslog(LOG_ERR,"cloudinit: no LAN device found");
 	exit(1);
 	exit(1);
 } else {
 } else {
 	$config['interfaces']['lan']['if'] = $lanDevice;
 	$config['interfaces']['lan']['if'] = $lanDevice;
 }
 }
 if ($netData['config'][1][0]['type'] == 'static') {
 if ($netData['config'][1][0]['type'] == 'static') {
 	$config['interfaces']['lan']['ipaddr'] = $netData['config'][1][0]['address'];
 	$config['interfaces']['lan']['ipaddr'] = $netData['config'][1][0]['address'];
-	$config['interfaces']['lan']['subnet'] = 32 - log((ip2long($netData['config'][1][0]['netmask']) ^ ip2long('255.255.255.255')) + 1 ,2);
+	$config['interfaces']['lan']['subnet'] = calcCIDR( $netData['config'][1][0]['netmask']);
 	$config['interfaces']['lan']['gateway'] = $netData['config'][1][0]['gateway'];
 	$config['interfaces']['lan']['gateway'] = $netData['config'][1][0]['gateway'];
 }
 }
 // configure additional network devices
 // configure additional network devices
@@ -203,14 +309,14 @@ if ($ifLastNr > 2) {
 		$optDeviceName = "opt" . strval($ifNr-1);
 		$optDeviceName = "opt" . strval($ifNr-1);
 		$optDevice = searchIfDevice( $netData['config'][$ifNr]['mac_address']);
 		$optDevice = searchIfDevice( $netData['config'][$ifNr]['mac_address']);
 		if (!$optDevice) {
 		if (!$optDevice) {
-			syslog(LOG_WARN,"cloud-init: given network device {$netData['config'][$ifNr]['mac_address']} not found");
+			syslog(LOG_WARN,"cloudinit: given network device {$netData['config'][$ifNr]['mac_address']} not found");
 			break;
 			break;
 		} else {
 		} else {
 			$config['interfaces'][$optDeviceName]['if'] = $optDevice;
 			$config['interfaces'][$optDeviceName]['if'] = $optDevice;
 		}
 		}
 		if ($netData['config'][$ifNr][0]['type'] == 'static') {
 		if ($netData['config'][$ifNr][0]['type'] == 'static') {
 			$config['interfaces'][$optDeviceName]['ipaddr'] = $netData['config'][$ifNr][0]['address'];
 			$config['interfaces'][$optDeviceName]['ipaddr'] = $netData['config'][$ifNr][0]['address'];
-			$config['interfaces'][$optDeviceName]['subnet'] = 32 - log((ip2long($netData['config'][$ifNr][0]['netmask']) ^ ip2long('255.255.255.255')) + 1 ,2);
+			$config['interfaces'][$optDeviceName]['subnet'] = calcCIDR( $netData['config'][$ifNr][0]['netmask']);
 			$config['interfaces'][$optDeviceName]['gateway'] = $netData['config'][$ifNr][0]['gateway'];
 			$config['interfaces'][$optDeviceName]['gateway'] = $netData['config'][$ifNr][0]['gateway'];
 		}
 		}
 	}
 	}
@@ -226,5 +332,9 @@ $config['system']['hostname'] = $userData['hostname'];
 $config['system']['user'][0]['bcrypt-hash'] = $userData['password'];
 $config['system']['user'][0]['bcrypt-hash'] = $userData['password'];
 // write the configuration
 // write the configuration
 write_config();
 write_config();
+// update runmode control file
+file_put_contents( "$cloudInitLocalPath/$cloudInitControl", "init");
+// skip pfSense wizard
+unlink("/conf/trigger_initial_wizard");
 // finally reboot the system
 // finally reboot the system
 system_reboot_sync();
 system_reboot_sync();