Add VMware vnc_over_websocket option (#9938)

This commit is contained in:
Sylvia Moss 2020-09-16 11:02:46 +02:00 committed by GitHub
parent bdd736b800
commit 7736ae6c67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1684 additions and 68 deletions

View File

@ -12,10 +12,11 @@ type BootConfigWrapper struct {
bootcommand.VNCConfig `mapstructure:",squash"`
// If set to true, Packer will use USB HID Keyboard scan codes to send the boot command to the VM and
// the [disable_vnc](#disable_vnc) option will be ignored and automatically set to true.
// This option is not supported by hosts with free license.
// This option is not supported by hosts running a free VMware license. As alternative please use [vnc_over_websocket](#vnc_over_websocket) instead.
//
// ~> **Note:** The ESXi 6.5+ removes support to VNC. In this case, the `usb_keyboard` should be set to true
// in order to send boot command keystrokes to the VM.
// ~> **Note:** The ESXi 6.7+ removes support to VNC. In this case, the `usb_keyboard` or [vnc_over_websocket](#vnc_over_websocket)
// should be set to true in order to send boot command keystrokes to the VM. If both are set, `usb_keyboard` will be ignored
// and set to false.
USBKeyBoard bool `mapstructure:"usb_keyboard"`
}

View File

@ -985,3 +985,11 @@ func (d *ESX5Driver) TypeOnKeyboard(input KeyInput) (int32, error) {
}
return resp.Returnval, nil
}
func (d *ESX5Driver) AcquireVNCOverWebsocketTicket() (*types.VirtualMachineTicket, error) {
vm, err := d.finder.VirtualMachine(d.ctx, d.VMName)
if err != nil {
return nil, err
}
return vm.AcquireTicket(d.ctx, string(types.VirtualMachineTicketTypeWebmks))
}

View File

@ -8,7 +8,9 @@ import (
"github.com/hashicorp/packer/template/interpolate"
)
// ~> **Note:** If [usb_keyboard](#usb_keyboard) is set to true, any VNC configuration will be ignored.
// ~> **Note:** If [usb_keyboard](#usb_keyboard) or [vnc_over_websocket](#vnc_over_websocket) is set to true,
// any other VNC configuration will be ignored. If both are set to true, [usb_keyboard](#usb_keyboard) will be ignored
// and Packer will connect to the VM with VNC over websocket.
type RunConfig struct {
// Packer defaults to building VMware virtual machines
// by launching a GUI that shows the console of the machine being built. When
@ -38,28 +40,54 @@ type RunConfig struct {
// true if building on ESXi 6.5 and 6.7 with VNC enabled. Defaults to
// false.
VNCDisablePassword bool `mapstructure:"vnc_disable_password" required:"false"`
// When set to true, Packer will connect to the remote VNC server over a websocket connection
// and any other VNC configuration option will be ignored.
// Remote builds using ESXi 6.7+ allows to connect to the VNC server only over websocket,
// for these the `vnc_over_websocket` must be set to true.
// If [usb_keyboard](#usb_keyboard) is also set to true, `usb_keyboard` will be ignored and set to false.
VNCOverWebsocket bool `mapstructure:"vnc_over_websocket" required:"false"`
// Do not validate VNC over websocket server's TLS certificate. Defaults to `false`.
InsecureConnection bool `mapstructure:"insecure_connection" required:"false"`
}
func (c *RunConfig) Prepare(_ *interpolate.Context, bootConfig *BootConfigWrapper) (errs []error) {
if !bootConfig.USBKeyBoard {
if c.VNCPortMin == 0 {
c.VNCPortMin = 5900
func (c *RunConfig) Prepare(_ *interpolate.Context, bootConfig *BootConfigWrapper, driverConfig *DriverConfig) (warnings []string, errs []error) {
if c.VNCOverWebsocket {
if driverConfig.RemoteType == "" {
errs = append(errs, fmt.Errorf("'vnc_over_websocket' can only be used with remote VMWare builds."))
return
}
if c.VNCPortMax == 0 {
c.VNCPortMax = 6000
}
if c.VNCBindAddress == "" {
c.VNCBindAddress = "127.0.0.1"
}
if c.VNCPortMin > c.VNCPortMax {
errs = append(errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
}
if c.VNCPortMin < 0 {
errs = append(errs, fmt.Errorf("vnc_port_min must be positive"))
if bootConfig.USBKeyBoard {
warnings = append(warnings, "[WARN] Both 'usb_keyboard' and 'vnc_over_websocket' are set. "+
"The `usb_keyboard` option will be ignored and automatically set to false.")
bootConfig.USBKeyBoard = false
}
}
if bootConfig.USBKeyBoard || c.VNCOverWebsocket {
if c.VNCPortMin != 0 || c.VNCPortMax != 0 || c.VNCBindAddress != "" || c.VNCDisablePassword {
warnings = append(warnings, "[WARN] When one of 'usb_keyboard' and 'vnc_over_websocket' is set "+
"any other VNC configuration will be ignored.")
}
return
}
if c.VNCPortMin == 0 {
c.VNCPortMin = 5900
}
if c.VNCPortMax == 0 {
c.VNCPortMax = 6000
}
if c.VNCBindAddress == "" {
c.VNCBindAddress = "127.0.0.1"
}
if c.VNCPortMin > c.VNCPortMax {
errs = append(errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
}
if c.VNCPortMin < 0 {
errs = append(errs, fmt.Errorf("vnc_port_min must be positive"))
}
return
}

View File

@ -0,0 +1,140 @@
package common
import (
"fmt"
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/packer/common/bootcommand"
"github.com/hashicorp/packer/template/interpolate"
)
func TestRunConfig_Prepare(t *testing.T) {
tc := []struct {
name string
config *RunConfig
expectedConfig *RunConfig
boot *BootConfigWrapper
driver *DriverConfig
errs []error
warnings []string
}{
{
name: "VNC dafaults",
config: &RunConfig{},
expectedConfig: &RunConfig{
VNCPortMin: 5900,
VNCPortMax: 6000,
VNCBindAddress: "127.0.0.1",
},
boot: new(BootConfigWrapper),
driver: new(DriverConfig),
errs: nil,
warnings: nil,
},
{
name: "VNC port min less than vnc port max",
config: &RunConfig{
VNCPortMin: 5000,
VNCPortMax: 5900,
},
expectedConfig: &RunConfig{
VNCPortMin: 5000,
VNCPortMax: 5900,
VNCBindAddress: "127.0.0.1",
},
boot: new(BootConfigWrapper),
driver: new(DriverConfig),
errs: nil,
warnings: nil,
},
{
name: "VNC port min bigger than vnc port max",
config: &RunConfig{
VNCPortMin: 5900,
VNCPortMax: 5000,
},
expectedConfig: nil,
boot: new(BootConfigWrapper),
driver: new(DriverConfig),
errs: []error{fmt.Errorf("vnc_port_min must be less than vnc_port_max")},
warnings: nil,
},
{
name: "VNC port min must be positive",
config: &RunConfig{
VNCPortMin: -1,
},
expectedConfig: nil,
boot: new(BootConfigWrapper),
driver: new(DriverConfig),
errs: []error{fmt.Errorf("vnc_port_min must be positive")},
warnings: nil,
},
{
name: "fail when vnc_over_websocket set when remote_type is not set",
config: &RunConfig{
VNCOverWebsocket: true,
},
expectedConfig: nil,
boot: new(BootConfigWrapper),
driver: new(DriverConfig),
errs: []error{fmt.Errorf("'vnc_over_websocket' can only be used with remote VMWare builds.")},
warnings: nil,
},
{
name: "choose vnc_over_websocket usb_keyboard",
config: &RunConfig{
VNCOverWebsocket: true,
},
expectedConfig: &RunConfig{
VNCOverWebsocket: true,
},
boot: &BootConfigWrapper{USBKeyBoard: true},
driver: &DriverConfig{RemoteType: "esxi"},
errs: nil,
warnings: []string{"[WARN] Both 'usb_keyboard' and 'vnc_over_websocket' are set. " +
"The `usb_keyboard` option will be ignored and automatically set to false."},
},
{
name: "warn about ignored vnc configuration",
config: &RunConfig{
VNCOverWebsocket: true,
VNCPortMin: 5000,
VNCPortMax: 5900,
},
expectedConfig: &RunConfig{
VNCOverWebsocket: true,
VNCPortMin: 5000,
VNCPortMax: 5900,
},
boot: new(BootConfigWrapper),
driver: &DriverConfig{RemoteType: "esxi"},
errs: nil,
warnings: []string{"[WARN] When one of 'usb_keyboard' and 'vnc_over_websocket' is set " +
"any other VNC configuration will be ignored."},
},
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
warnings, errs := c.config.Prepare(interpolate.NewContext(), c.boot, c.driver)
if !reflect.DeepEqual(errs, c.errs) {
t.Fatalf("bad: \n expected '%v' \nactual '%v'", c.errs, errs)
}
if diff := cmp.Diff(warnings, c.warnings); diff != "" {
t.Fatalf("unexpected warnings: %s", diff)
}
if len(c.errs) == 0 {
if diff := cmp.Diff(c.config, c.expectedConfig,
cmpopts.IgnoreFields(bootcommand.VNCConfig{},
"BootConfig",
)); diff != "" {
t.Fatalf("unexpected config: %s", diff)
}
}
})
}
}

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"log"
"net"
"time"
"github.com/hashicorp/packer/common/bootcommand"
@ -44,9 +43,8 @@ func (s *StepVNCBootCommand) Run(ctx context.Context, state multistep.StateBag)
debug := state.Get("debug").(bool)
httpPort := state.Get("http_port").(int)
ui := state.Get("ui").(packer.Ui)
vncIp := state.Get("vnc_ip").(string)
vncPort := state.Get("vnc_port").(int)
vncPassword := state.Get("vnc_password")
conn := state.Get("vnc_conn").(*vnc.ClientConn)
defer conn.Close()
// Wait the for the vm to boot.
if int64(s.Config.BootWait) > 0 {
@ -58,43 +56,11 @@ func (s *StepVNCBootCommand) Run(ctx context.Context, state multistep.StateBag)
return multistep.ActionHalt
}
}
var pauseFn multistep.DebugPauseFn
if debug {
pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn)
}
// Connect to VNC
ui.Say(fmt.Sprintf("Connecting to VM via VNC (%s:%d)", vncIp, vncPort))
nc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", vncIp, vncPort))
if err != nil {
err := fmt.Errorf("Error connecting to VNC: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
defer nc.Close()
var auth []vnc.ClientAuth
if vncPassword != nil && len(vncPassword.(string)) > 0 {
auth = []vnc.ClientAuth{&vnc.PasswordAuth{Password: vncPassword.(string)}}
} else {
auth = []vnc.ClientAuth{new(vnc.ClientAuthNone)}
}
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)
ui.Error(err.Error())
return multistep.ActionHalt
}
defer c.Close()
log.Printf("Connected to VNC desktop: %s", c.DesktopName)
hostIP := state.Get("http_ip").(string)
s.Ctx.Data = &VNCBootCommandTemplateData{
HTTPIP: hostIP,
@ -102,7 +68,7 @@ func (s *StepVNCBootCommand) Run(ctx context.Context, state multistep.StateBag)
Name: s.VMName,
}
d := bootcommand.NewVNCDriver(c, s.Config.BootKeyInterval)
d := bootcommand.NewVNCDriver(conn, s.Config.BootKeyInterval)
ui.Say("Typing the boot command over VNC...")
flatBootCommand := s.Config.FlatBootCommand()

View File

@ -0,0 +1,141 @@
package common
import (
"context"
"crypto/tls"
"fmt"
"log"
"net"
"net/url"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/go-vnc"
"golang.org/x/net/websocket"
)
type StepVNCConnect struct {
VNCEnabled bool
VNCOverWebsocket bool
InsecureConnection bool
DriverConfig *DriverConfig
}
func (s *StepVNCConnect) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if !s.VNCOverWebsocket && !s.VNCEnabled {
return multistep.ActionContinue
}
ui := state.Get("ui").(packer.Ui)
var c *vnc.ClientConn
var err error
if s.VNCOverWebsocket {
ui.Say("Connecting to VNC over websocket...")
c, err = s.ConnectVNCOverWebsocketClient(state)
} else {
ui.Say("Connecting to VNC...")
c, err = s.ConnectVNC(state)
}
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state.Put("vnc_conn", c)
return multistep.ActionContinue
}
func (s *StepVNCConnect) ConnectVNCOverWebsocketClient(state multistep.StateBag) (*vnc.ClientConn, error) {
driver := state.Get("driver").(*ESX5Driver)
// Acquire websocket ticket
ticket, err := driver.AcquireVNCOverWebsocketTicket()
if err != nil {
err := fmt.Errorf("Error acquiring vnc over websocket ticket: %s", err)
state.Put("error", err)
return nil, err
}
host := ticket.Host
if len(host) == 0 {
host = s.DriverConfig.RemoteHost
}
port := ticket.Port
if port == 0 {
port = 443
}
websocketUrl := fmt.Sprintf("wss://%s:%d/ticket/%s", host, port, ticket.Ticket)
log.Printf("[DEBUG] websocket url: %s", websocketUrl)
u, err := url.Parse(websocketUrl)
if err != nil {
err := fmt.Errorf("Error parsing websocket url: %s\n", err)
state.Put("error", err)
return nil, err
}
origin, err := url.Parse("http://localhost")
if err != nil {
err := fmt.Errorf("Error parsing websocket origin url: %s\n", err)
state.Put("error", err)
return nil, err
}
// Create the websocket connection and set it to a BinaryFrame
websocketConfig := &websocket.Config{
Location: u,
Origin: origin,
TlsConfig: &tls.Config{InsecureSkipVerify: s.InsecureConnection},
Version: websocket.ProtocolVersionHybi13,
Protocol: []string{"binary"},
}
nc, err := websocket.DialConfig(websocketConfig)
if err != nil {
err := fmt.Errorf("Error Dialing: %s\n", err)
state.Put("error", err)
return nil, err
}
nc.PayloadType = websocket.BinaryFrame
// Setup the VNC connection over the websocket
ccconfig := &vnc.ClientConfig{
Auth: []vnc.ClientAuth{new(vnc.ClientAuthNone)},
Exclusive: false,
}
c, err := vnc.Client(nc, ccconfig)
if err != nil {
err := fmt.Errorf("Error setting the VNC over websocket client: %s\n", err)
state.Put("error", err)
return nil, err
}
return c, nil
}
func (s *StepVNCConnect) ConnectVNC(state multistep.StateBag) (*vnc.ClientConn, error) {
vncIp := state.Get("vnc_ip").(string)
vncPort := state.Get("vnc_port").(int)
vncPassword := state.Get("vnc_password")
nc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", vncIp, vncPort))
if err != nil {
err := fmt.Errorf("Error connecting to VNC: %s", err)
state.Put("error", err)
return nil, err
}
auth := []vnc.ClientAuth{new(vnc.ClientAuthNone)}
if vncPassword != nil && len(vncPassword.(string)) > 0 {
auth = []vnc.ClientAuth{&vnc.PasswordAuth{Password: vncPassword.(string)}}
}
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)
return nil, err
}
return c, nil
}
func (s *StepVNCConnect) Cleanup(multistep.StateBag) {}

View File

@ -138,7 +138,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
HTTPAddress: b.config.HTTPAddress,
},
&vmwcommon.StepConfigureVNC{
Enabled: !b.config.DisableVNC,
Enabled: !b.config.DisableVNC && !b.config.VNCOverWebsocket,
VNCBindAddress: b.config.VNCBindAddress,
VNCPortMin: b.config.VNCPortMin,
VNCPortMax: b.config.VNCPortMax,
@ -153,6 +153,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
DurationBeforeStop: 5 * time.Second,
Headless: b.config.Headless,
},
&vmwcommon.StepVNCConnect{
VNCEnabled: !b.config.DisableVNC,
VNCOverWebsocket: b.config.VNCOverWebsocket,
InsecureConnection: b.config.InsecureConnection,
DriverConfig: &b.config.DriverConfig,
},
stepBootCommand,
&communicator.StepConnect{
Config: &b.config.SSHConfig.Comm,

View File

@ -99,7 +99,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
vncWarnings, vncErrs := c.BootConfigWrapper.Prepare(&c.ctx, &c.DriverConfig)
warnings = append(warnings, vncWarnings...)
errs = packer.MultiErrorAppend(errs, vncErrs...)
runConfigWarnings, runConfigErrs := c.RunConfig.Prepare(&c.ctx, &c.BootConfigWrapper, &c.DriverConfig)
warnings = append(warnings, runConfigWarnings...)
errs = packer.MultiErrorAppend(errs, runConfigErrs...)
isoWarnings, isoErrs := c.ISOConfig.Prepare(&c.ctx)
warnings = append(warnings, isoWarnings...)
errs = packer.MultiErrorAppend(errs, isoErrs...)
@ -107,7 +109,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, c.HWConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(&c.ctx, &c.PackerConfig)...)
errs = packer.MultiErrorAppend(errs, c.DriverConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(&c.ctx, &c.BootConfigWrapper)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ToolsConfig.Prepare(&c.ctx)...)

View File

@ -66,6 +66,8 @@ type FlatConfig struct {
VNCPortMin *int `mapstructure:"vnc_port_min" required:"false" cty:"vnc_port_min" hcl:"vnc_port_min"`
VNCPortMax *int `mapstructure:"vnc_port_max" cty:"vnc_port_max" hcl:"vnc_port_max"`
VNCDisablePassword *bool `mapstructure:"vnc_disable_password" required:"false" cty:"vnc_disable_password" hcl:"vnc_disable_password"`
VNCOverWebsocket *bool `mapstructure:"vnc_over_websocket" required:"false" cty:"vnc_over_websocket" hcl:"vnc_over_websocket"`
InsecureConnection *bool `mapstructure:"insecure_connection" required:"false" cty:"insecure_connection" hcl:"insecure_connection"`
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"`
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"`
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
@ -209,6 +211,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"vnc_port_min": &hcldec.AttrSpec{Name: "vnc_port_min", Type: cty.Number, Required: false},
"vnc_port_max": &hcldec.AttrSpec{Name: "vnc_port_max", Type: cty.Number, Required: false},
"vnc_disable_password": &hcldec.AttrSpec{Name: "vnc_disable_password", Type: cty.Bool, Required: false},
"vnc_over_websocket": &hcldec.AttrSpec{Name: "vnc_over_websocket", Type: cty.Bool, Required: false},
"insecure_connection": &hcldec.AttrSpec{Name: "insecure_connection", Type: cty.Bool, Required: false},
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},

View File

@ -131,7 +131,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
RemoteType: b.config.RemoteType,
},
&vmwcommon.StepConfigureVNC{
Enabled: !b.config.DisableVNC,
Enabled: !b.config.DisableVNC && !b.config.VNCOverWebsocket,
VNCBindAddress: b.config.VNCBindAddress,
VNCPortMin: b.config.VNCPortMin,
VNCPortMax: b.config.VNCPortMax,
@ -146,6 +146,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
DurationBeforeStop: 5 * time.Second,
Headless: b.config.Headless,
},
&vmwcommon.StepVNCConnect{
VNCEnabled: !b.config.DisableVNC,
VNCOverWebsocket: b.config.VNCOverWebsocket,
InsecureConnection: b.config.InsecureConnection,
DriverConfig: &b.config.DriverConfig,
},
stepBootCommand,
&communicator.StepConnect{
Config: &b.config.SSHConfig.Comm,

View File

@ -84,10 +84,12 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
vncWarnings, vncErrs := c.BootConfigWrapper.Prepare(&c.ctx, &c.DriverConfig)
warnings = append(warnings, vncWarnings...)
errs = packer.MultiErrorAppend(errs, vncErrs...)
runConfigWarnings, runConfigErrs := c.RunConfig.Prepare(&c.ctx, &c.BootConfigWrapper, &c.DriverConfig)
warnings = append(warnings, runConfigWarnings...)
errs = packer.MultiErrorAppend(errs, runConfigErrs...)
errs = packer.MultiErrorAppend(errs, c.DriverConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(&c.ctx, &c.PackerConfig)...)
errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(&c.ctx, &c.BootConfigWrapper)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ToolsConfig.Prepare(&c.ctx)...)

View File

@ -51,6 +51,8 @@ type FlatConfig struct {
VNCPortMin *int `mapstructure:"vnc_port_min" required:"false" cty:"vnc_port_min" hcl:"vnc_port_min"`
VNCPortMax *int `mapstructure:"vnc_port_max" cty:"vnc_port_max" hcl:"vnc_port_max"`
VNCDisablePassword *bool `mapstructure:"vnc_disable_password" required:"false" cty:"vnc_disable_password" hcl:"vnc_disable_password"`
VNCOverWebsocket *bool `mapstructure:"vnc_over_websocket" required:"false" cty:"vnc_over_websocket" hcl:"vnc_over_websocket"`
InsecureConnection *bool `mapstructure:"insecure_connection" required:"false" cty:"insecure_connection" hcl:"insecure_connection"`
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"`
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"`
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
@ -175,6 +177,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"vnc_port_min": &hcldec.AttrSpec{Name: "vnc_port_min", Type: cty.Number, Required: false},
"vnc_port_max": &hcldec.AttrSpec{Name: "vnc_port_max", Type: cty.Number, Required: false},
"vnc_disable_password": &hcldec.AttrSpec{Name: "vnc_disable_password", Type: cty.Bool, Required: false},
"vnc_over_websocket": &hcldec.AttrSpec{Name: "vnc_over_websocket", Type: cty.Bool, Required: false},
"insecure_connection": &hcldec.AttrSpec{Name: "insecure_connection", Type: cty.Bool, Required: false},
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},

4
go.mod
View File

@ -39,6 +39,9 @@ require (
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/go-resty/resty/v2 v2.3.0
github.com/gobwas/glob v0.2.3
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.0.4 // indirect
github.com/gofrs/flock v0.7.3
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/golang/protobuf v1.4.2 // indirect
@ -49,6 +52,7 @@ require (
github.com/gophercloud/gophercloud v0.12.0
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026
github.com/hashicorp/consul/api v1.4.0

16
go.sum
View File

@ -202,6 +202,12 @@ github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gofrs/flock v0.7.3 h1:I0EKY9l8HZCXTMYC4F80vwT6KNypV9uYKP3Alm/hjmQ=
github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
@ -278,6 +284,8 @@ github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c h1:iawx2ojEQA7c+
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 h1:JIM+OacoOJRU30xpjMf8sulYqjr0ViA3WDrTX6j/yDI=
github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ=
@ -579,6 +587,14 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/sylviamoss/go-vnc v0.0.0-20200908143403-27ce26f2bed9 h1:uEIT4Mi8C8O+S44150F3lug+WaZ01aJ0sLHvN4npMsc=
github.com/sylviamoss/go-vnc v0.0.0-20200908143403-27ce26f2bed9/go.mod h1:+I9wdVxvb9OqA4vpPvYoLvAeSx+Ew5PF6SiOWekjkGE=
github.com/sylviamoss/go-vnc v0.0.0-20200908143844-452d6786ec66 h1:4pQ2Ex/3Fxn7WDvpa85XqHLkc+cSoceIefX/8mD518o=
github.com/sylviamoss/go-vnc v0.0.0-20200908143844-452d6786ec66/go.mod h1:+I9wdVxvb9OqA4vpPvYoLvAeSx+Ew5PF6SiOWekjkGE=
github.com/sylviamoss/go-vnc v0.0.0-20200908144818-694f171108bf h1:HUHmcnxuPAJktJM1acw5AckmPvOWz/BoenBLB+IMuo0=
github.com/sylviamoss/go-vnc v0.0.0-20200908144818-694f171108bf/go.mod h1:+I9wdVxvb9OqA4vpPvYoLvAeSx+Ew5PF6SiOWekjkGE=
github.com/sylviamoss/go-vnc v0.0.0-20200908145922-2061e37ffa00 h1:mURYI59JxRHmjEKY4ZZING6qm6e/R91tUqdwFmHoAjY=
github.com/sylviamoss/go-vnc v0.0.0-20200908145922-2061e37ffa00/go.mod h1:+I9wdVxvb9OqA4vpPvYoLvAeSx+Ew5PF6SiOWekjkGE=
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible h1:bs+0lcG4RELNbE8PsBC9oaPP0/qExr0DuEGnZyocm84=
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8=

106
vendor/golang.org/x/net/websocket/client.go generated vendored Normal file
View File

@ -0,0 +1,106 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bufio"
"io"
"net"
"net/http"
"net/url"
)
// DialError is an error that occurs while dialling a websocket server.
type DialError struct {
*Config
Err error
}
func (e *DialError) Error() string {
return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error()
}
// NewConfig creates a new WebSocket config for client connection.
func NewConfig(server, origin string) (config *Config, err error) {
config = new(Config)
config.Version = ProtocolVersionHybi13
config.Location, err = url.ParseRequestURI(server)
if err != nil {
return
}
config.Origin, err = url.ParseRequestURI(origin)
if err != nil {
return
}
config.Header = http.Header(make(map[string][]string))
return
}
// NewClient creates a new WebSocket client connection over rwc.
func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
br := bufio.NewReader(rwc)
bw := bufio.NewWriter(rwc)
err = hybiClientHandshake(config, br, bw)
if err != nil {
return
}
buf := bufio.NewReadWriter(br, bw)
ws = newHybiClientConn(config, buf, rwc)
return
}
// Dial opens a new client connection to a WebSocket.
func Dial(url_, protocol, origin string) (ws *Conn, err error) {
config, err := NewConfig(url_, origin)
if err != nil {
return nil, err
}
if protocol != "" {
config.Protocol = []string{protocol}
}
return DialConfig(config)
}
var portMap = map[string]string{
"ws": "80",
"wss": "443",
}
func parseAuthority(location *url.URL) string {
if _, ok := portMap[location.Scheme]; ok {
if _, _, err := net.SplitHostPort(location.Host); err != nil {
return net.JoinHostPort(location.Host, portMap[location.Scheme])
}
}
return location.Host
}
// DialConfig opens a new client connection to a WebSocket with a config.
func DialConfig(config *Config) (ws *Conn, err error) {
var client net.Conn
if config.Location == nil {
return nil, &DialError{config, ErrBadWebSocketLocation}
}
if config.Origin == nil {
return nil, &DialError{config, ErrBadWebSocketOrigin}
}
dialer := config.Dialer
if dialer == nil {
dialer = &net.Dialer{}
}
client, err = dialWithDialer(dialer, config)
if err != nil {
goto Error
}
ws, err = NewClient(config, client)
if err != nil {
client.Close()
goto Error
}
return
Error:
return nil, &DialError{config, err}
}

24
vendor/golang.org/x/net/websocket/dial.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"crypto/tls"
"net"
)
func dialWithDialer(dialer *net.Dialer, config *Config) (conn net.Conn, err error) {
switch config.Location.Scheme {
case "ws":
conn, err = dialer.Dial("tcp", parseAuthority(config.Location))
case "wss":
conn, err = tls.DialWithDialer(dialer, "tcp", parseAuthority(config.Location), config.TlsConfig)
default:
err = ErrBadScheme
}
return
}

583
vendor/golang.org/x/net/websocket/hybi.go generated vendored Normal file
View File

@ -0,0 +1,583 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
// This file implements a protocol of hybi draft.
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
import (
"bufio"
"bytes"
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
const (
websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
closeStatusNormal = 1000
closeStatusGoingAway = 1001
closeStatusProtocolError = 1002
closeStatusUnsupportedData = 1003
closeStatusFrameTooLarge = 1004
closeStatusNoStatusRcvd = 1005
closeStatusAbnormalClosure = 1006
closeStatusBadMessageData = 1007
closeStatusPolicyViolation = 1008
closeStatusTooBigData = 1009
closeStatusExtensionMismatch = 1010
maxControlFramePayloadLength = 125
)
var (
ErrBadMaskingKey = &ProtocolError{"bad masking key"}
ErrBadPongMessage = &ProtocolError{"bad pong message"}
ErrBadClosingStatus = &ProtocolError{"bad closing status"}
ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"}
ErrNotImplemented = &ProtocolError{"not implemented"}
handshakeHeader = map[string]bool{
"Host": true,
"Upgrade": true,
"Connection": true,
"Sec-Websocket-Key": true,
"Sec-Websocket-Origin": true,
"Sec-Websocket-Version": true,
"Sec-Websocket-Protocol": true,
"Sec-Websocket-Accept": true,
}
)
// A hybiFrameHeader is a frame header as defined in hybi draft.
type hybiFrameHeader struct {
Fin bool
Rsv [3]bool
OpCode byte
Length int64
MaskingKey []byte
data *bytes.Buffer
}
// A hybiFrameReader is a reader for hybi frame.
type hybiFrameReader struct {
reader io.Reader
header hybiFrameHeader
pos int64
length int
}
func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
n, err = frame.reader.Read(msg)
if frame.header.MaskingKey != nil {
for i := 0; i < n; i++ {
msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4]
frame.pos++
}
}
return n, err
}
func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
func (frame *hybiFrameReader) HeaderReader() io.Reader {
if frame.header.data == nil {
return nil
}
if frame.header.data.Len() == 0 {
return nil
}
return frame.header.data
}
func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
func (frame *hybiFrameReader) Len() (n int) { return frame.length }
// A hybiFrameReaderFactory creates new frame reader based on its frame type.
type hybiFrameReaderFactory struct {
*bufio.Reader
}
// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
// See Section 5.2 Base Framing protocol for detail.
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
hybiFrame := new(hybiFrameReader)
frame = hybiFrame
var header []byte
var b byte
// First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
b, err = buf.ReadByte()
if err != nil {
return
}
header = append(header, b)
hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0
for i := 0; i < 3; i++ {
j := uint(6 - i)
hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0
}
hybiFrame.header.OpCode = header[0] & 0x0f
// Second byte. Mask/Payload len(7bits)
b, err = buf.ReadByte()
if err != nil {
return
}
header = append(header, b)
mask := (b & 0x80) != 0
b &= 0x7f
lengthFields := 0
switch {
case b <= 125: // Payload length 7bits.
hybiFrame.header.Length = int64(b)
case b == 126: // Payload length 7+16bits
lengthFields = 2
case b == 127: // Payload length 7+64bits
lengthFields = 8
}
for i := 0; i < lengthFields; i++ {
b, err = buf.ReadByte()
if err != nil {
return
}
if lengthFields == 8 && i == 0 { // MSB must be zero when 7+64 bits
b &= 0x7f
}
header = append(header, b)
hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b)
}
if mask {
// Masking key. 4 bytes.
for i := 0; i < 4; i++ {
b, err = buf.ReadByte()
if err != nil {
return
}
header = append(header, b)
hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b)
}
}
hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length)
hybiFrame.header.data = bytes.NewBuffer(header)
hybiFrame.length = len(header) + int(hybiFrame.header.Length)
return
}
// A HybiFrameWriter is a writer for hybi frame.
type hybiFrameWriter struct {
writer *bufio.Writer
header *hybiFrameHeader
}
func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
var header []byte
var b byte
if frame.header.Fin {
b |= 0x80
}
for i := 0; i < 3; i++ {
if frame.header.Rsv[i] {
j := uint(6 - i)
b |= 1 << j
}
}
b |= frame.header.OpCode
header = append(header, b)
if frame.header.MaskingKey != nil {
b = 0x80
} else {
b = 0
}
lengthFields := 0
length := len(msg)
switch {
case length <= 125:
b |= byte(length)
case length < 65536:
b |= 126
lengthFields = 2
default:
b |= 127
lengthFields = 8
}
header = append(header, b)
for i := 0; i < lengthFields; i++ {
j := uint((lengthFields - i - 1) * 8)
b = byte((length >> j) & 0xff)
header = append(header, b)
}
if frame.header.MaskingKey != nil {
if len(frame.header.MaskingKey) != 4 {
return 0, ErrBadMaskingKey
}
header = append(header, frame.header.MaskingKey...)
frame.writer.Write(header)
data := make([]byte, length)
for i := range data {
data[i] = msg[i] ^ frame.header.MaskingKey[i%4]
}
frame.writer.Write(data)
err = frame.writer.Flush()
return length, err
}
frame.writer.Write(header)
frame.writer.Write(msg)
err = frame.writer.Flush()
return length, err
}
func (frame *hybiFrameWriter) Close() error { return nil }
type hybiFrameWriterFactory struct {
*bufio.Writer
needMaskingKey bool
}
func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
if buf.needMaskingKey {
frameHeader.MaskingKey, err = generateMaskingKey()
if err != nil {
return nil, err
}
}
return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
}
type hybiFrameHandler struct {
conn *Conn
payloadType byte
}
func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (frameReader, error) {
if handler.conn.IsServerConn() {
// The client MUST mask all frames sent to the server.
if frame.(*hybiFrameReader).header.MaskingKey == nil {
handler.WriteClose(closeStatusProtocolError)
return nil, io.EOF
}
} else {
// The server MUST NOT mask all frames.
if frame.(*hybiFrameReader).header.MaskingKey != nil {
handler.WriteClose(closeStatusProtocolError)
return nil, io.EOF
}
}
if header := frame.HeaderReader(); header != nil {
io.Copy(ioutil.Discard, header)
}
switch frame.PayloadType() {
case ContinuationFrame:
frame.(*hybiFrameReader).header.OpCode = handler.payloadType
case TextFrame, BinaryFrame:
handler.payloadType = frame.PayloadType()
case CloseFrame:
return nil, io.EOF
case PingFrame, PongFrame:
b := make([]byte, maxControlFramePayloadLength)
n, err := io.ReadFull(frame, b)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
return nil, err
}
io.Copy(ioutil.Discard, frame)
if frame.PayloadType() == PingFrame {
if _, err := handler.WritePong(b[:n]); err != nil {
return nil, err
}
}
return nil, nil
}
return frame, nil
}
func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
handler.conn.wio.Lock()
defer handler.conn.wio.Unlock()
w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame)
if err != nil {
return err
}
msg := make([]byte, 2)
binary.BigEndian.PutUint16(msg, uint16(status))
_, err = w.Write(msg)
w.Close()
return err
}
func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
handler.conn.wio.Lock()
defer handler.conn.wio.Unlock()
w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame)
if err != nil {
return 0, err
}
n, err = w.Write(msg)
w.Close()
return n, err
}
// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
if buf == nil {
br := bufio.NewReader(rwc)
bw := bufio.NewWriter(rwc)
buf = bufio.NewReadWriter(br, bw)
}
ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
frameWriterFactory: hybiFrameWriterFactory{
buf.Writer, request == nil},
PayloadType: TextFrame,
defaultCloseStatus: closeStatusNormal}
ws.frameHandler = &hybiFrameHandler{conn: ws}
return ws
}
// generateMaskingKey generates a masking key for a frame.
func generateMaskingKey() (maskingKey []byte, err error) {
maskingKey = make([]byte, 4)
if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
return
}
return
}
// generateNonce generates a nonce consisting of a randomly selected 16-byte
// value that has been base64-encoded.
func generateNonce() (nonce []byte) {
key := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
panic(err)
}
nonce = make([]byte, 24)
base64.StdEncoding.Encode(nonce, key)
return
}
// removeZone removes IPv6 zone identifer from host.
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
func removeZone(host string) string {
if !strings.HasPrefix(host, "[") {
return host
}
i := strings.LastIndex(host, "]")
if i < 0 {
return host
}
j := strings.LastIndex(host[:i], "%")
if j < 0 {
return host
}
return host[:j] + host[i:]
}
// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
func getNonceAccept(nonce []byte) (expected []byte, err error) {
h := sha1.New()
if _, err = h.Write(nonce); err != nil {
return
}
if _, err = h.Write([]byte(websocketGUID)); err != nil {
return
}
expected = make([]byte, 28)
base64.StdEncoding.Encode(expected, h.Sum(nil))
return
}
// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
// According to RFC 6874, an HTTP client, proxy, or other
// intermediary must remove any IPv6 zone identifier attached
// to an outgoing URI.
bw.WriteString("Host: " + removeZone(config.Location.Host) + "\r\n")
bw.WriteString("Upgrade: websocket\r\n")
bw.WriteString("Connection: Upgrade\r\n")
nonce := generateNonce()
if config.handshakeData != nil {
nonce = []byte(config.handshakeData["key"])
}
bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
if config.Version != ProtocolVersionHybi13 {
return ErrBadProtocolVersion
}
bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
if len(config.Protocol) > 0 {
bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
}
// TODO(ukai): send Sec-WebSocket-Extensions.
err = config.Header.WriteSubset(bw, handshakeHeader)
if err != nil {
return err
}
bw.WriteString("\r\n")
if err = bw.Flush(); err != nil {
return err
}
resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
if err != nil {
return err
}
if resp.StatusCode != 101 {
return ErrBadStatus
}
if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
return ErrBadUpgrade
}
expectedAccept, err := getNonceAccept(nonce)
if err != nil {
return err
}
if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
return ErrChallengeResponse
}
if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
return ErrUnsupportedExtensions
}
offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
if offeredProtocol != "" {
protocolMatched := false
for i := 0; i < len(config.Protocol); i++ {
if config.Protocol[i] == offeredProtocol {
protocolMatched = true
break
}
}
if !protocolMatched {
return ErrBadWebSocketProtocol
}
config.Protocol = []string{offeredProtocol}
}
return nil
}
// newHybiClientConn creates a client WebSocket connection after handshake.
func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
return newHybiConn(config, buf, rwc, nil)
}
// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
type hybiServerHandshaker struct {
*Config
accept []byte
}
func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
c.Version = ProtocolVersionHybi13
if req.Method != "GET" {
return http.StatusMethodNotAllowed, ErrBadRequestMethod
}
// HTTP version can be safely ignored.
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
return http.StatusBadRequest, ErrNotWebSocket
}
key := req.Header.Get("Sec-Websocket-Key")
if key == "" {
return http.StatusBadRequest, ErrChallengeResponse
}
version := req.Header.Get("Sec-Websocket-Version")
switch version {
case "13":
c.Version = ProtocolVersionHybi13
default:
return http.StatusBadRequest, ErrBadWebSocketVersion
}
var scheme string
if req.TLS != nil {
scheme = "wss"
} else {
scheme = "ws"
}
c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI())
if err != nil {
return http.StatusBadRequest, err
}
protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
if protocol != "" {
protocols := strings.Split(protocol, ",")
for i := 0; i < len(protocols); i++ {
c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
}
}
c.accept, err = getNonceAccept([]byte(key))
if err != nil {
return http.StatusInternalServerError, err
}
return http.StatusSwitchingProtocols, nil
}
// Origin parses the Origin header in req.
// If the Origin header is not set, it returns nil and nil.
func Origin(config *Config, req *http.Request) (*url.URL, error) {
var origin string
switch config.Version {
case ProtocolVersionHybi13:
origin = req.Header.Get("Origin")
}
if origin == "" {
return nil, nil
}
return url.ParseRequestURI(origin)
}
func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
if len(c.Protocol) > 0 {
if len(c.Protocol) != 1 {
// You need choose a Protocol in Handshake func in Server.
return ErrBadWebSocketProtocol
}
}
buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
buf.WriteString("Upgrade: websocket\r\n")
buf.WriteString("Connection: Upgrade\r\n")
buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
if len(c.Protocol) > 0 {
buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
}
// TODO(ukai): send Sec-WebSocket-Extensions.
if c.Header != nil {
err := c.Header.WriteSubset(buf, handshakeHeader)
if err != nil {
return err
}
}
buf.WriteString("\r\n")
return buf.Flush()
}
func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
return newHybiServerConn(c.Config, buf, rwc, request)
}
// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
return newHybiConn(config, buf, rwc, request)
}

113
vendor/golang.org/x/net/websocket/server.go generated vendored Normal file
View File

@ -0,0 +1,113 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bufio"
"fmt"
"io"
"net/http"
)
func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) {
var hs serverHandshaker = &hybiServerHandshaker{Config: config}
code, err := hs.ReadHandshake(buf.Reader, req)
if err == ErrBadWebSocketVersion {
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion)
buf.WriteString("\r\n")
buf.WriteString(err.Error())
buf.Flush()
return
}
if err != nil {
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
buf.WriteString("\r\n")
buf.WriteString(err.Error())
buf.Flush()
return
}
if handshake != nil {
err = handshake(config, req)
if err != nil {
code = http.StatusForbidden
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
buf.WriteString("\r\n")
buf.Flush()
return
}
}
err = hs.AcceptHandshake(buf.Writer)
if err != nil {
code = http.StatusBadRequest
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
buf.WriteString("\r\n")
buf.Flush()
return
}
conn = hs.NewServerConn(buf, rwc, req)
return
}
// Server represents a server of a WebSocket.
type Server struct {
// Config is a WebSocket configuration for new WebSocket connection.
Config
// Handshake is an optional function in WebSocket handshake.
// For example, you can check, or don't check Origin header.
// Another example, you can select config.Protocol.
Handshake func(*Config, *http.Request) error
// Handler handles a WebSocket connection.
Handler
}
// ServeHTTP implements the http.Handler interface for a WebSocket
func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s.serveWebSocket(w, req)
}
func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) {
rwc, buf, err := w.(http.Hijacker).Hijack()
if err != nil {
panic("Hijack failed: " + err.Error())
}
// The server should abort the WebSocket connection if it finds
// the client did not send a handshake that matches with protocol
// specification.
defer rwc.Close()
conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake)
if err != nil {
return
}
if conn == nil {
panic("unexpected nil conn")
}
s.Handler(conn)
}
// Handler is a simple interface to a WebSocket browser client.
// It checks if Origin header is valid URL by default.
// You might want to verify websocket.Conn.Config().Origin in the func.
// If you use Server instead of Handler, you could call websocket.Origin and
// check the origin in your Handshake func. So, if you want to accept
// non-browser clients, which do not send an Origin header, set a
// Server.Handshake that does not check the origin.
type Handler func(*Conn)
func checkOrigin(config *Config, req *http.Request) (err error) {
config.Origin, err = Origin(config, req)
if err == nil && config.Origin == nil {
return fmt.Errorf("null origin")
}
return err
}
// ServeHTTP implements the http.Handler interface for a WebSocket
func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
s := Server{Handler: h, Handshake: checkOrigin}
s.serveWebSocket(w, req)
}

451
vendor/golang.org/x/net/websocket/websocket.go generated vendored Normal file
View File

@ -0,0 +1,451 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package websocket implements a client and server for the WebSocket protocol
// as specified in RFC 6455.
//
// This package currently lacks some features found in alternative
// and more actively maintained WebSocket packages:
//
// https://godoc.org/github.com/gorilla/websocket
// https://godoc.org/nhooyr.io/websocket
package websocket // import "golang.org/x/net/websocket"
import (
"bufio"
"crypto/tls"
"encoding/json"
"errors"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"sync"
"time"
)
const (
ProtocolVersionHybi13 = 13
ProtocolVersionHybi = ProtocolVersionHybi13
SupportedProtocolVersion = "13"
ContinuationFrame = 0
TextFrame = 1
BinaryFrame = 2
CloseFrame = 8
PingFrame = 9
PongFrame = 10
UnknownFrame = 255
DefaultMaxPayloadBytes = 32 << 20 // 32MB
)
// ProtocolError represents WebSocket protocol errors.
type ProtocolError struct {
ErrorString string
}
func (err *ProtocolError) Error() string { return err.ErrorString }
var (
ErrBadProtocolVersion = &ProtocolError{"bad protocol version"}
ErrBadScheme = &ProtocolError{"bad scheme"}
ErrBadStatus = &ProtocolError{"bad status"}
ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"}
ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"}
ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"}
ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"}
ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"}
ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"}
ErrBadFrame = &ProtocolError{"bad frame"}
ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"}
ErrNotWebSocket = &ProtocolError{"not websocket protocol"}
ErrBadRequestMethod = &ProtocolError{"bad method"}
ErrNotSupported = &ProtocolError{"not supported"}
)
// ErrFrameTooLarge is returned by Codec's Receive method if payload size
// exceeds limit set by Conn.MaxPayloadBytes
var ErrFrameTooLarge = errors.New("websocket: frame payload size exceeds limit")
// Addr is an implementation of net.Addr for WebSocket.
type Addr struct {
*url.URL
}
// Network returns the network type for a WebSocket, "websocket".
func (addr *Addr) Network() string { return "websocket" }
// Config is a WebSocket configuration
type Config struct {
// A WebSocket server address.
Location *url.URL
// A Websocket client origin.
Origin *url.URL
// WebSocket subprotocols.
Protocol []string
// WebSocket protocol version.
Version int
// TLS config for secure WebSocket (wss).
TlsConfig *tls.Config
// Additional header fields to be sent in WebSocket opening handshake.
Header http.Header
// Dialer used when opening websocket connections.
Dialer *net.Dialer
handshakeData map[string]string
}
// serverHandshaker is an interface to handle WebSocket server side handshake.
type serverHandshaker interface {
// ReadHandshake reads handshake request message from client.
// Returns http response code and error if any.
ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error)
// AcceptHandshake accepts the client handshake request and sends
// handshake response back to client.
AcceptHandshake(buf *bufio.Writer) (err error)
// NewServerConn creates a new WebSocket connection.
NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn)
}
// frameReader is an interface to read a WebSocket frame.
type frameReader interface {
// Reader is to read payload of the frame.
io.Reader
// PayloadType returns payload type.
PayloadType() byte
// HeaderReader returns a reader to read header of the frame.
HeaderReader() io.Reader
// TrailerReader returns a reader to read trailer of the frame.
// If it returns nil, there is no trailer in the frame.
TrailerReader() io.Reader
// Len returns total length of the frame, including header and trailer.
Len() int
}
// frameReaderFactory is an interface to creates new frame reader.
type frameReaderFactory interface {
NewFrameReader() (r frameReader, err error)
}
// frameWriter is an interface to write a WebSocket frame.
type frameWriter interface {
// Writer is to write payload of the frame.
io.WriteCloser
}
// frameWriterFactory is an interface to create new frame writer.
type frameWriterFactory interface {
NewFrameWriter(payloadType byte) (w frameWriter, err error)
}
type frameHandler interface {
HandleFrame(frame frameReader) (r frameReader, err error)
WriteClose(status int) (err error)
}
// Conn represents a WebSocket connection.
//
// Multiple goroutines may invoke methods on a Conn simultaneously.
type Conn struct {
config *Config
request *http.Request
buf *bufio.ReadWriter
rwc io.ReadWriteCloser
rio sync.Mutex
frameReaderFactory
frameReader
wio sync.Mutex
frameWriterFactory
frameHandler
PayloadType byte
defaultCloseStatus int
// MaxPayloadBytes limits the size of frame payload received over Conn
// by Codec's Receive method. If zero, DefaultMaxPayloadBytes is used.
MaxPayloadBytes int
}
// Read implements the io.Reader interface:
// it reads data of a frame from the WebSocket connection.
// if msg is not large enough for the frame data, it fills the msg and next Read
// will read the rest of the frame data.
// it reads Text frame or Binary frame.
func (ws *Conn) Read(msg []byte) (n int, err error) {
ws.rio.Lock()
defer ws.rio.Unlock()
again:
if ws.frameReader == nil {
frame, err := ws.frameReaderFactory.NewFrameReader()
if err != nil {
return 0, err
}
ws.frameReader, err = ws.frameHandler.HandleFrame(frame)
if err != nil {
return 0, err
}
if ws.frameReader == nil {
goto again
}
}
n, err = ws.frameReader.Read(msg)
if err == io.EOF {
if trailer := ws.frameReader.TrailerReader(); trailer != nil {
io.Copy(ioutil.Discard, trailer)
}
ws.frameReader = nil
goto again
}
return n, err
}
// Write implements the io.Writer interface:
// it writes data as a frame to the WebSocket connection.
func (ws *Conn) Write(msg []byte) (n int, err error) {
ws.wio.Lock()
defer ws.wio.Unlock()
w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType)
if err != nil {
return 0, err
}
n, err = w.Write(msg)
w.Close()
return n, err
}
// Close implements the io.Closer interface.
func (ws *Conn) Close() error {
err := ws.frameHandler.WriteClose(ws.defaultCloseStatus)
err1 := ws.rwc.Close()
if err != nil {
return err
}
return err1
}
// IsClientConn reports whether ws is a client-side connection.
func (ws *Conn) IsClientConn() bool { return ws.request == nil }
// IsServerConn reports whether ws is a server-side connection.
func (ws *Conn) IsServerConn() bool { return ws.request != nil }
// LocalAddr returns the WebSocket Origin for the connection for client, or
// the WebSocket location for server.
func (ws *Conn) LocalAddr() net.Addr {
if ws.IsClientConn() {
return &Addr{ws.config.Origin}
}
return &Addr{ws.config.Location}
}
// RemoteAddr returns the WebSocket location for the connection for client, or
// the Websocket Origin for server.
func (ws *Conn) RemoteAddr() net.Addr {
if ws.IsClientConn() {
return &Addr{ws.config.Location}
}
return &Addr{ws.config.Origin}
}
var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
// SetDeadline sets the connection's network read & write deadlines.
func (ws *Conn) SetDeadline(t time.Time) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetDeadline(t)
}
return errSetDeadline
}
// SetReadDeadline sets the connection's network read deadline.
func (ws *Conn) SetReadDeadline(t time.Time) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetReadDeadline(t)
}
return errSetDeadline
}
// SetWriteDeadline sets the connection's network write deadline.
func (ws *Conn) SetWriteDeadline(t time.Time) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetWriteDeadline(t)
}
return errSetDeadline
}
// Config returns the WebSocket config.
func (ws *Conn) Config() *Config { return ws.config }
// Request returns the http request upgraded to the WebSocket.
// It is nil for client side.
func (ws *Conn) Request() *http.Request { return ws.request }
// Codec represents a symmetric pair of functions that implement a codec.
type Codec struct {
Marshal func(v interface{}) (data []byte, payloadType byte, err error)
Unmarshal func(data []byte, payloadType byte, v interface{}) (err error)
}
// Send sends v marshaled by cd.Marshal as single frame to ws.
func (cd Codec) Send(ws *Conn, v interface{}) (err error) {
data, payloadType, err := cd.Marshal(v)
if err != nil {
return err
}
ws.wio.Lock()
defer ws.wio.Unlock()
w, err := ws.frameWriterFactory.NewFrameWriter(payloadType)
if err != nil {
return err
}
_, err = w.Write(data)
w.Close()
return err
}
// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores
// in v. The whole frame payload is read to an in-memory buffer; max size of
// payload is defined by ws.MaxPayloadBytes. If frame payload size exceeds
// limit, ErrFrameTooLarge is returned; in this case frame is not read off wire
// completely. The next call to Receive would read and discard leftover data of
// previous oversized frame before processing next frame.
func (cd Codec) Receive(ws *Conn, v interface{}) (err error) {
ws.rio.Lock()
defer ws.rio.Unlock()
if ws.frameReader != nil {
_, err = io.Copy(ioutil.Discard, ws.frameReader)
if err != nil {
return err
}
ws.frameReader = nil
}
again:
frame, err := ws.frameReaderFactory.NewFrameReader()
if err != nil {
return err
}
frame, err = ws.frameHandler.HandleFrame(frame)
if err != nil {
return err
}
if frame == nil {
goto again
}
maxPayloadBytes := ws.MaxPayloadBytes
if maxPayloadBytes == 0 {
maxPayloadBytes = DefaultMaxPayloadBytes
}
if hf, ok := frame.(*hybiFrameReader); ok && hf.header.Length > int64(maxPayloadBytes) {
// payload size exceeds limit, no need to call Unmarshal
//
// set frameReader to current oversized frame so that
// the next call to this function can drain leftover
// data before processing the next frame
ws.frameReader = frame
return ErrFrameTooLarge
}
payloadType := frame.PayloadType()
data, err := ioutil.ReadAll(frame)
if err != nil {
return err
}
return cd.Unmarshal(data, payloadType, v)
}
func marshal(v interface{}) (msg []byte, payloadType byte, err error) {
switch data := v.(type) {
case string:
return []byte(data), TextFrame, nil
case []byte:
return data, BinaryFrame, nil
}
return nil, UnknownFrame, ErrNotSupported
}
func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
switch data := v.(type) {
case *string:
*data = string(msg)
return nil
case *[]byte:
*data = msg
return nil
}
return ErrNotSupported
}
/*
Message is a codec to send/receive text/binary data in a frame on WebSocket connection.
To send/receive text frame, use string type.
To send/receive binary frame, use []byte type.
Trivial usage:
import "websocket"
// receive text frame
var message string
websocket.Message.Receive(ws, &message)
// send text frame
message = "hello"
websocket.Message.Send(ws, message)
// receive binary frame
var data []byte
websocket.Message.Receive(ws, &data)
// send binary frame
data = []byte{0, 1, 2}
websocket.Message.Send(ws, data)
*/
var Message = Codec{marshal, unmarshal}
func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) {
msg, err = json.Marshal(v)
return msg, TextFrame, err
}
func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
return json.Unmarshal(msg, v)
}
/*
JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.
Trivial usage:
import "websocket"
type T struct {
Msg string
Count int
}
// receive JSON type T
var data T
websocket.JSON.Receive(ws, &data)
// send JSON type T
websocket.JSON.Send(ws, data)
*/
var JSON = Codec{jsonMarshal, jsonUnmarshal}

1
vendor/modules.txt vendored
View File

@ -722,6 +722,7 @@ golang.org/x/net/internal/timeseries
golang.org/x/net/proxy
golang.org/x/net/publicsuffix
golang.org/x/net/trace
golang.org/x/net/websocket
# golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/oauth2
golang.org/x/oauth2/google

View File

@ -2,7 +2,8 @@
- `usb_keyboard` (bool) - If set to true, Packer will use USB HID Keyboard scan codes to send the boot command to the VM and
the [disable_vnc](#disable_vnc) option will be ignored and automatically set to true.
This option is not supported by hosts with free license.
This option is not supported by hosts running a free VMware license. As alternative please use [vnc_over_websocket](#vnc_over_websocket) instead.
~> **Note:** The ESXi 6.5+ removes support to VNC. In this case, the `usb_keyboard` should be set to true
in order to send boot command keystrokes to the VM.
~> **Note:** The ESXi 6.7+ removes support to VNC. In this case, the `usb_keyboard` or [vnc_over_websocket](#vnc_over_websocket)
should be set to true in order to send boot command keystrokes to the VM. If both are set, `usb_keyboard` will be ignored
and set to false.

View File

@ -28,3 +28,11 @@
is used to secure the VNC communication with the VM. This must be set to
true if building on ESXi 6.5 and 6.7 with VNC enabled. Defaults to
false.
- `vnc_over_websocket` (bool) - When set to true, Packer will connect to the remote VNC server over a websocket connection
and any other VNC configuration option will be ignored.
Remote builds using ESXi 6.7+ allows to connect to the VNC server only over websocket,
for these the `vnc_over_websocket` must be set to true.
If [usb_keyboard](#usb_keyboard) is also set to true, `usb_keyboard` will be ignored and set to false.
- `insecure_connection` (bool) - Do not validate VNC over websocket server's TLS certificate. Defaults to `false`.

View File

@ -1,3 +1,5 @@
<!-- Code generated from the comments of the RunConfig struct in builder/vmware/common/run_config.go; DO NOT EDIT MANUALLY -->
~> **Note:** If [usb_keyboard](#usb_keyboard) is set to true, any VNC configuration will be ignored.
~> **Note:** If [usb_keyboard](#usb_keyboard) or [vnc_over_websocket](#vnc_over_websocket) is set to true,
any other VNC configuration will be ignored. If both are set to true, [usb_keyboard](#usb_keyboard) will be ignored
and Packer will connect to the VM with VNC over websocket.