diff --git a/builder/vmware/common/step_configure_vnc.go b/builder/vmware/common/step_configure_vnc.go index b9a81cf06..6ebb21cb3 100644 --- a/builder/vmware/common/step_configure_vnc.go +++ b/builder/vmware/common/step_configure_vnc.go @@ -30,7 +30,7 @@ type VNCAddressFinder interface { VNCAddress(string, uint, uint) (string, uint, error) // UpdateVMX, sets driver specific VNC values to VMX data. - UpdateVMX(vncAddress string, vncPort uint, vmxData map[string]string) + UpdateVMX(vncAddress, vncPassword string, vncPort uint, vmxData map[string]string) } func (StepConfigureVNC) VNCAddress(vncBindAddress string, portMin, portMax uint) (string, uint, error) { @@ -56,6 +56,21 @@ func (StepConfigureVNC) VNCAddress(vncBindAddress string, portMin, portMax uint) return vncBindAddress, vncPort, nil } +func VNCPassword() string { + length := int(8) + + charSet := []byte("1234567890-=qwertyuiop[]asdfghjkl;zxcvbnm,./!@#%^*()_+QWERTYUIOP{}|ASDFGHJKL:XCVBNM<>?") + charSetLength := len(charSet) + + password := make([]byte, length) + + for i := 0; i < length; i++ { + password[i] = charSet[rand.Intn(charSetLength)] + } + + return string(password) +} + func (s *StepConfigureVNC) Run(state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) @@ -91,10 +106,12 @@ func (s *StepConfigureVNC) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } + vncPassword := VNCPassword() + log.Printf("Found available VNC port: %d", vncPort) vmxData := ParseVMX(string(vmxBytes)) - vncFinder.UpdateVMX(vncBindAddress, vncPort, vmxData) + vncFinder.UpdateVMX(vncBindAddress, vncPassword, vncPort, vmxData) if err := WriteVMX(vmxPath, vmxData); err != nil { err := fmt.Errorf("Error writing VMX data: %s", err) @@ -105,14 +122,16 @@ func (s *StepConfigureVNC) Run(state multistep.StateBag) multistep.StepAction { state.Put("vnc_port", vncPort) state.Put("vnc_ip", vncBindAddress) + state.Put("vnc_password", vncPassword) return multistep.ActionContinue } -func (StepConfigureVNC) UpdateVMX(address string, port uint, data map[string]string) { +func (StepConfigureVNC) UpdateVMX(address, password string, port uint, data map[string]string) { data["remotedisplay.vnc.enabled"] = "TRUE" data["remotedisplay.vnc.port"] = fmt.Sprintf("%d", port) data["remotedisplay.vnc.ip"] = address + data["remotedisplay.vnc.password"] = password } func (StepConfigureVNC) Cleanup(multistep.StateBag) { diff --git a/builder/vmware/common/step_configure_vnc.go.rej b/builder/vmware/common/step_configure_vnc.go.rej new file mode 100644 index 000000000..621d524fd --- /dev/null +++ b/builder/vmware/common/step_configure_vnc.go.rej @@ -0,0 +1,46 @@ +diff a/builder/vmware/common/step_configure_vnc.go b/builder/vmware/common/step_configure_vnc.go (rejected hunks) +@@ -52,6 +52,21 @@ func (StepConfigureVNC) VNCAddress(portMin, portMax uint) (string, uint, error) + return "127.0.0.1", vncPort, nil + } + ++func VNCPassword() (string) { ++ length := int(8) ++ ++ charSet := []byte("1234567890-=qwertyuiop[]asdfghjkl;zxcvbnm,./!@#%^*()_+QWERTYUIOP{}|ASDFGHJKL:XCVBNM<>?") ++ charSetLength := len(charSet) ++ ++ password := make([]byte, length) ++ ++ for i := 0; i < length; i++ { ++ password[i] = charSet[ rand.Intn(charSetLength) ] ++ } ++ ++ return string(password) ++} ++ + func (s *StepConfigureVNC) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) + ui := state.Get("ui").(packer.Ui) +@@ -86,12 +101,14 @@ func (s *StepConfigureVNC) Run(state multistep.StateBag) multistep.StepAction { + ui.Error(err.Error()) + return multistep.ActionHalt + } ++ vncPassword := VNCPassword() + + log.Printf("Found available VNC port: %d", vncPort) + + vmxData := ParseVMX(string(vmxBytes)) + vmxData["remotedisplay.vnc.enabled"] = "TRUE" + vmxData["remotedisplay.vnc.port"] = fmt.Sprintf("%d", vncPort) ++ vmxData["remotedisplay.vnc.password"] = vncPassword + + if err := WriteVMX(vmxPath, vmxData); err != nil { + err := fmt.Errorf("Error writing VMX data: %s", err) +@@ -102,6 +119,7 @@ func (s *StepConfigureVNC) Run(state multistep.StateBag) multistep.StepAction { + + state.Put("vnc_port", vncPort) + state.Put("vnc_ip", vncIp) ++ state.Put("vnc_password", vncPassword) + + return multistep.ActionContinue + } diff --git a/builder/vmware/common/step_configure_vnc_test.go b/builder/vmware/common/step_configure_vnc_test.go index 373f44e44..a89b75999 100644 --- a/builder/vmware/common/step_configure_vnc_test.go +++ b/builder/vmware/common/step_configure_vnc_test.go @@ -12,7 +12,7 @@ func TestStepConfigureVNC_implVNCAddressFinder(t *testing.T) { func TestStepConfigureVNC_UpdateVMX(t *testing.T) { var s StepConfigureVNC data := make(map[string]string) - s.UpdateVMX("0.0.0.0", 5900, data) + s.UpdateVMX("0.0.0.0", "", 5900, data) if ip := data["remotedisplay.vnc.ip"]; ip != "0.0.0.0" { t.Errorf("bad VMX data for key remotedisplay.vnc.ip: %v", ip) } diff --git a/builder/vmware/common/step_run.go b/builder/vmware/common/step_run.go index 4e996d833..7784ea431 100644 --- a/builder/vmware/common/step_run.go +++ b/builder/vmware/common/step_run.go @@ -38,15 +38,17 @@ func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction { if s.Headless { vncIpRaw, vncIpOk := state.GetOk("vnc_ip") vncPortRaw, vncPortOk := state.GetOk("vnc_port") + vncPasswordRaw, vncPasswordOk := state.GetOk("vnc_password") - if vncIpOk && vncPortOk { + if vncIpOk && vncPortOk && vncPasswordOk { vncIp := vncIpRaw.(string) vncPort := vncPortRaw.(uint) + vncPassword := vncPasswordRaw.(string) ui.Message(fmt.Sprintf( "The VM will be run headless, without a GUI. If you want to\n"+ - "view the screen of the VM, connect via VNC without a password to\n"+ - "%s:%d", vncIp, vncPort)) + "view the screen of the VM, connect via VNC with the password %s to\n"+ + "%s:%d", vncPassword, vncIp, vncPort)) } else { ui.Message("The VM will be run headless, without a GUI, as configured.\n" + "If the run isn't succeeding as you expect, please enable the GUI\n" + diff --git a/builder/vmware/common/step_type_boot_command.go b/builder/vmware/common/step_type_boot_command.go index 9750aaa71..44e589f4b 100644 --- a/builder/vmware/common/step_type_boot_command.go +++ b/builder/vmware/common/step_type_boot_command.go @@ -46,6 +46,7 @@ func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction ui := state.Get("ui").(packer.Ui) vncIp := state.Get("vnc_ip").(string) vncPort := state.Get("vnc_port").(uint) + vncPassword := state.Get("vnc_password") var pauseFn multistep.DebugPauseFn if debug { @@ -63,7 +64,15 @@ func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction } defer nc.Close() - c, err := vnc.Client(nc, &vnc.ClientConfig{Exclusive: false}) + var auth []vnc.ClientAuth + + if vncPassword != nil { + auth = []vnc.ClientAuth{&vnc.PasswordAuth{Password: vncPassword.(string)}} + } else { + auth = []vnc.ClientAuth{} + } + + c, err := vnc.Client(nc, &vnc.ClientConfig{Auth: auth, Exclusive: true}) if err != nil { err := fmt.Errorf("Error handshaking with VNC: %s", err) state.Put("error", err) diff --git a/builder/vmware/common/step_type_boot_command.go.rej b/builder/vmware/common/step_type_boot_command.go.rej new file mode 100644 index 000000000..ee084c6bb --- /dev/null +++ b/builder/vmware/common/step_type_boot_command.go.rej @@ -0,0 +1,26 @@ +diff a/builder/vmware/common/step_type_boot_command.go b/builder/vmware/common/step_type_boot_command.go (rejected hunks) +@@ -45,6 +45,7 @@ func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction + ui := state.Get("ui").(packer.Ui) + vncIp := state.Get("vnc_ip").(string) + vncPort := state.Get("vnc_port").(uint) ++ vncPassword := state.Get("vnc_password") + + // Connect to VNC + ui.Say("Connecting to VM via VNC") +@@ -57,7 +58,15 @@ func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction + } + defer nc.Close() + +- c, err := vnc.Client(nc, &vnc.ClientConfig{Exclusive: true}) ++ var auth []vnc.ClientAuth ++ ++ if vncPassword != nil { ++ auth = []vnc.ClientAuth{&vnc.PasswordAuth{Password: vncPassword.(string)}} ++ } else { ++ auth = []vnc.ClientAuth{} ++ } ++ ++ c, err := vnc.Client(nc, &vnc.ClientConfig{Auth: auth, Exclusive: true}) + if err != nil { + err := fmt.Errorf("Error handshaking with VNC: %s", err) + state.Put("error", err) diff --git a/builder/vmware/iso/driver_esx5.go b/builder/vmware/iso/driver_esx5.go index fa8c5df12..5efd4aad6 100644 --- a/builder/vmware/iso/driver_esx5.go +++ b/builder/vmware/iso/driver_esx5.go @@ -231,10 +231,11 @@ func (d *ESX5Driver) VNCAddress(_ string, portMin, portMax uint) (string, uint, } // UpdateVMX, adds the VNC port to the VMX data. -func (ESX5Driver) UpdateVMX(_ string, port uint, data map[string]string) { +func (ESX5Driver) UpdateVMX(_, password string, port uint, data map[string]string) { // Do not set remotedisplay.vnc.ip - this breaks ESXi. data["remotedisplay.vnc.enabled"] = "TRUE" data["remotedisplay.vnc.port"] = fmt.Sprintf("%d", port) + data["remotedisplay.vnc.password"] = password } func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { diff --git a/builder/vmware/iso/driver_esx5_test.go b/builder/vmware/iso/driver_esx5_test.go index 937124a25..e1fabe414 100644 --- a/builder/vmware/iso/driver_esx5_test.go +++ b/builder/vmware/iso/driver_esx5_test.go @@ -13,6 +13,22 @@ func TestESX5Driver_implDriver(t *testing.T) { var _ vmwcommon.Driver = new(ESX5Driver) } +func TestESX5Driver_UpdateVMX(t *testing.T) { + var driver ESX5Driver + data := make(map[string]string) + driver.UpdateVMX("0.0.0.0", "", 5900, data) + if _, ok := data["remotedisplay.vnc.ip"]; ok { + // Do not add the remotedisplay.vnc.ip on ESXi + t.Fatal("invalid VMX data key: remotedisplay.vnc.ip") + } + if enabled := data["remotedisplay.vnc.enabled"]; enabled != "TRUE" { + t.Errorf("bad VMX data for key remotedisplay.vnc.enabled: %v", enabled) + } + if port := data["remotedisplay.vnc.port"]; port != fmt.Sprint(port) { + t.Errorf("bad VMX data for key remotedisplay.vnc.port: %v", port) + } +} + func TestESX5Driver_implOutputDir(t *testing.T) { var _ vmwcommon.OutputDir = new(ESX5Driver) }