func (d *ESX5Driver) connect() error { address := fmt.Sprintf("%s:%d", d.Host, d.Port) auth := []gossh.AuthMethod{ gossh.Password(d.Password), gossh.KeyboardInteractive( ssh.PasswordKeyboardInteractive(d.Password)), } // TODO(dougm) KeyPath support sshConfig := &ssh.Config{ Connection: ssh.ConnectFunc("tcp", address), SSHConfig: &gossh.ClientConfig{ User: d.Username, Auth: auth, }, NoPty: true, } comm, err := ssh.New(address, sshConfig) if err != nil { return err } d.comm = comm return nil }
func (d *ESX5Driver) connect() error { address := fmt.Sprintf("%s:%d", d.Host, d.Port) auth := []gossh.AuthMethod{ gossh.Password(d.Password), gossh.KeyboardInteractive( ssh.PasswordKeyboardInteractive(d.Password)), } if d.PrivateKey != "" { signer, err := commonssh.FileSigner(d.PrivateKey) if err != nil { return err } auth = append(auth, gossh.PublicKeys(signer)) } sshConfig := &ssh.Config{ Connection: ssh.ConnectFunc("tcp", address), SSHConfig: &gossh.ClientConfig{ User: d.Username, Auth: auth, }, } comm, err := ssh.New(address, sshConfig) if err != nil { return err } d.comm = comm return nil }
func (p *PostProcessor) connect() error { address := fmt.Sprintf("%s:%d", p.config.Host, p.config.SshPort) auth := []gossh.AuthMethod{ gossh.Password(p.config.Password), gossh.KeyboardInteractive( ssh.PasswordKeyboardInteractive(p.config.Password)), } // TODO(dougm) KeyPath support sshConfig := &ssh.Config{ Connection: ssh.ConnectFunc("tcp", address), SSHConfig: &gossh.ClientConfig{ User: p.config.Username, Auth: auth, }, } comm, err := ssh.New(address, sshConfig) if err != nil { return err } p.comm = comm return nil }
func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) { // Determine if we're using a bastion host, and if so, retrieve // that configuration. This configuration doesn't change so we // do this one before entering the retry loop. var bProto, bAddr string var bConf *gossh.ClientConfig if s.Config.SSHBastionHost != "" { // The protocol is hardcoded for now, but may be configurable one day bProto = "tcp" bAddr = fmt.Sprintf( "%s:%d", s.Config.SSHBastionHost, s.Config.SSHBastionPort) conf, err := sshBastionConfig(s.Config) if err != nil { return nil, fmt.Errorf("Error configuring bastion: %s", err) } bConf = conf } handshakeAttempts := 0 var comm packer.Communicator first := true for { // Don't check for cancel or wait on first iteration if !first { select { case <-cancel: log.Println("[DEBUG] SSH wait cancelled. Exiting loop.") return nil, errors.New("SSH wait cancelled") case <-time.After(5 * time.Second): } } first = false // First we request the TCP connection information host, err := s.Host(state) if err != nil { log.Printf("[DEBUG] Error getting SSH address: %s", err) continue } port := s.Config.SSHPort if s.SSHPort != nil { port, err = s.SSHPort(state) if err != nil { log.Printf("[DEBUG] Error getting SSH port: %s", err) continue } } // Retrieve the SSH configuration sshConfig, err := s.SSHConfig(state) if err != nil { log.Printf("[DEBUG] Error getting SSH config: %s", err) continue } // Attempt to connect to SSH port var connFunc func() (net.Conn, error) address := fmt.Sprintf("%s:%d", host, port) if bAddr != "" { // We're using a bastion host, so use the bastion connfunc connFunc = ssh.BastionConnectFunc( bProto, bAddr, bConf, "tcp", address) } else { // No bastion host, connect directly connFunc = ssh.ConnectFunc("tcp", address) } nc, err := connFunc() if err != nil { log.Printf("[DEBUG] TCP connection to SSH ip/port failed: %s", err) continue } nc.Close() // Then we attempt to connect via SSH config := &ssh.Config{ Connection: connFunc, SSHConfig: sshConfig, Pty: s.Config.SSHPty, DisableAgent: s.Config.SSHDisableAgent, } log.Println("[INFO] Attempting SSH connection...") comm, err = ssh.New(address, config) if err != nil { log.Printf("[DEBUG] SSH handshake err: %s", err) // Only count this as an attempt if we were able to attempt // to authenticate. Note this is very brittle since it depends // on the string of the error... but I don't see any other way. if strings.Contains(err.Error(), "authenticate") { log.Printf( "[DEBUG] Detected authentication error. Increasing handshake attempts.") handshakeAttempts += 1 } if handshakeAttempts < s.Config.SSHHandshakeAttempts { // Try to connect via SSH a handful of times. We sleep here // so we don't get a ton of authentication errors back to back. time.Sleep(2 * time.Second) continue } return nil, err } break } return comm, nil }
func (s *stepConnectSSH) Run(state map[string]interface{}) multistep.StepAction { config := state["config"].(config) privateKey := state["privateKey"].(string) ui := state["ui"].(packer.Ui) ipAddress := state["droplet_ip"] // Build the keyring for authentication. This stores the private key // we'll use to authenticate. keyring := &ssh.SimpleKeychain{} err := keyring.AddPEMKey(privateKey) if err != nil { err := fmt.Errorf("Error setting up SSH config: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } connFunc := ssh.ConnectFunc( "tcp", fmt.Sprintf("%s:%d", ipAddress, config.SSHPort), 5*time.Minute) // Build the actual SSH client configuration sshConfig := &ssh.Config{ Connection: connFunc, SSHConfig: &gossh.ClientConfig{ User: config.SSHUsername, Auth: []gossh.ClientAuth{ gossh.ClientAuthKeyring(keyring), }, }, } // Start trying to connect to SSH connected := make(chan error, 1) connectQuit := make(chan bool, 1) defer func() { connectQuit <- true }() var comm packer.Communicator go func() { ui.Say("Connecting to the droplet via SSH...") attempts := 0 handshakeAttempts := 0 for { select { case <-connectQuit: return default: } // A brief sleep so we're not being overly zealous attempting // to connect to the instance. time.Sleep(500 * time.Millisecond) attempts += 1 nc, err := connFunc() if err != nil { continue } nc.Close() log.Println("TCP connection made. Attempting SSH handshake.") comm, err = ssh.New(sshConfig) if err == nil { log.Println("Connected to SSH!") break } handshakeAttempts += 1 log.Printf("SSH handshake error: %s", err) if handshakeAttempts > 5 { connected <- err return } } connected <- nil }() log.Printf("Waiting up to %s for SSH connection", config.sshTimeout) timeout := time.After(config.sshTimeout) ConnectWaitLoop: for { select { case err := <-connected: if err != nil { err := fmt.Errorf("Error connecting to SSH: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } // We connected. Just break the loop. break ConnectWaitLoop case <-timeout: err := errors.New("Timeout waiting for SSH to become available.") state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt case <-time.After(1 * time.Second): if _, ok := state[multistep.StateCancelled]; ok { log.Println("Interrupt detected, quitting waiting for SSH.") return multistep.ActionHalt } } } // Set the communicator on the state bag so it can be used later s.comm = comm state["communicator"] = comm return multistep.ActionContinue }
// This blocks until SSH becomes available, and sends the communicator // on the given channel. func (s *stepWaitForSSH) waitForSSH(state map[string]interface{}) (packer.Communicator, error) { config := state["config"].(*config) ui := state["ui"].(packer.Ui) sshHostPort := state["sshHostPort"].(uint) ui.Say("Waiting for SSH to become available...") var comm packer.Communicator var nc net.Conn for { if nc != nil { nc.Close() } time.Sleep(5 * time.Second) if s.cancel { log.Println("SSH wait cancelled. Exiting loop.") return nil, errors.New("SSH wait cancelled") } // Attempt to connect to SSH port nc, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", sshHostPort)) if err != nil { log.Printf("TCP connection to SSH ip/port failed: %s", err) continue } // Then we attempt to connect via SSH sshConfig := &gossh.ClientConfig{ User: config.SSHUser, Auth: []gossh.ClientAuth{ gossh.ClientAuthPassword(ssh.Password(config.SSHPassword)), gossh.ClientAuthKeyboardInteractive( ssh.PasswordKeyboardInteractive(config.SSHPassword)), }, } sshConnectSuccess := make(chan bool, 1) go func() { comm, err = ssh.New(nc, sshConfig) if err != nil { log.Printf("SSH connection fail: %s", err) sshConnectSuccess <- false return } sshConnectSuccess <- true }() select { case success := <-sshConnectSuccess: if !success { continue } case <-time.After(5 * time.Second): log.Printf("SSH handshake timeout. Trying again.") continue } ui.Say("Connected via SSH!") break } // Store the connection so we can close it later s.conn = nc return comm, nil }
func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) { handshakeAttempts := 0 var comm packer.Communicator for { select { case <-cancel: log.Println("SSH wait cancelled. Exiting loop.") return nil, errors.New("SSH wait cancelled") case <-time.After(5 * time.Second): } // First we request the TCP connection information address, err := s.SSHAddress(state) if err != nil { log.Printf("Error getting SSH address: %s", err) continue } // Retrieve the SSH configuration sshConfig, err := s.SSHConfig(state) if err != nil { log.Printf("Error getting SSH config: %s", err) continue } // Attempt to connect to SSH port connFunc := ssh.ConnectFunc("tcp", address) nc, err := connFunc() if err != nil { log.Printf("TCP connection to SSH ip/port failed: %s", err) continue } nc.Close() // Then we attempt to connect via SSH config := &ssh.Config{ Connection: connFunc, SSHConfig: sshConfig, NoPty: s.NoPty, } log.Println("Attempting SSH connection...") comm, err = ssh.New(config) if err != nil { log.Printf("SSH handshake err: %s", err) // Only count this as an attempt if we were able to attempt // to authenticate. Note this is very brittle since it depends // on the string of the error... but I don't see any other way. if strings.Contains(err.Error(), "authenticate") { log.Printf("Detected authentication error. Increasing handshake attempts.") handshakeAttempts += 1 } if handshakeAttempts < 10 { // Try to connect via SSH a handful of times continue } return nil, err } break } return comm, nil }
// This blocks until SSH becomes available, and sends the communicator // on the given channel. func (s *stepConnectSSH) waitForSSH(state map[string]interface{}) (packer.Communicator, error) { config := state["config"].(config) instance := state["instance"].(*ec2.Instance) privateKey := state["privateKey"].(string) ui := state["ui"].(packer.Ui) // Build the keyring for authentication. This stores the private key // we'll use to authenticate. keyring := &ssh.SimpleKeychain{} err := keyring.AddPEMKey(privateKey) if err != nil { return nil, fmt.Errorf("Error setting up SSH config: %s", err) } // Create the function that will be used to create the connection connFunc := ssh.ConnectFunc( "tcp", fmt.Sprintf("%s:%d", instance.DNSName, config.SSHPort), 5*time.Minute) ui.Say("Waiting for SSH to become available...") var comm packer.Communicator for { time.Sleep(5 * time.Second) if s.cancel { log.Println("SSH wait cancelled. Exiting loop.") return nil, errors.New("SSH wait cancelled") } // First just attempt a normal TCP connection that we close right // away. We just test this in order to wait for the TCP port to be ready. nc, err := connFunc() if err != nil { log.Printf("TCP connection to SSH ip/port failed: %s", err) continue } nc.Close() // Build the configuration to connect to SSH config := &ssh.Config{ Connection: connFunc, SSHConfig: &gossh.ClientConfig{ User: config.SSHUsername, Auth: []gossh.ClientAuth{ gossh.ClientAuthKeyring(keyring), }, }, } sshConnectSuccess := make(chan bool, 1) go func() { comm, err = ssh.New(config) if err != nil { log.Printf("SSH connection fail: %s", err) sshConnectSuccess <- false return } sshConnectSuccess <- true }() select { case success := <-sshConnectSuccess: if !success { continue } case <-time.After(5 * time.Second): log.Printf("SSH handshake timeout. Trying again.") continue } ui.Say("Connected via SSH!") break } return comm, nil }
// This blocks until SSH becomes available, and sends the communicator // on the given channel. func (s *stepWaitForSSH) waitForSSH(state map[string]interface{}) (packer.Communicator, error) { config := state["config"].(*config) ui := state["ui"].(packer.Ui) vmxPath := state["vmx_path"].(string) handshakeAttempts := 0 ui.Say("Waiting for SSH to become available...") var comm packer.Communicator for { time.Sleep(5 * time.Second) if s.cancel { log.Println("SSH wait cancelled. Exiting loop.") return nil, errors.New("SSH wait cancelled") } // First we wait for the IP to become available... log.Println("Lookup up IP information...") ipLookup, err := s.dhcpLeaseLookup(vmxPath) if err != nil { log.Printf("Can't lookup via DHCP lease: %s", err) } ip, err := ipLookup.GuestIP() if err != nil { log.Printf("IP lookup failed: %s", err) continue } log.Printf("Detected IP: %s", ip) // Attempt to connect to SSH port connFunc := ssh.ConnectFunc( "tcp", fmt.Sprintf("%s:%d", ip, config.SSHPort), 5*time.Minute) nc, err := connFunc() if err != nil { log.Printf("TCP connection to SSH ip/port failed: %s", err) continue } nc.Close() // Then we attempt to connect via SSH config := &ssh.Config{ Connection: connFunc, SSHConfig: &gossh.ClientConfig{ User: config.SSHUser, Auth: []gossh.ClientAuth{ gossh.ClientAuthPassword(ssh.Password(config.SSHPassword)), gossh.ClientAuthKeyboardInteractive( ssh.PasswordKeyboardInteractive(config.SSHPassword)), }, }, } comm, err = ssh.New(config) if err != nil { log.Printf("SSH handshake err: %s", err) handshakeAttempts += 1 if handshakeAttempts < 10 { // Try to connect via SSH a handful of times continue } return nil, err } ui.Say("Connected via SSH!") break } return comm, nil }
// This blocks until SSH becomes available, and sends the communicator // on the given channel. func (s *stepConnectSSH) waitForSSH(state map[string]interface{}) (packer.Communicator, error) { config := state["config"].(config) instance := state["instance"].(*ec2.Instance) privateKey := state["privateKey"].(string) ui := state["ui"].(packer.Ui) // Build the keyring for authentication. This stores the private key // we'll use to authenticate. keyring := &ssh.SimpleKeychain{} err := keyring.AddPEMKey(privateKey) if err != nil { return nil, fmt.Errorf("Error setting up SSH config: %s", err) } ui.Say("Waiting for SSH to become available...") var comm packer.Communicator var nc net.Conn for { if nc != nil { nc.Close() } time.Sleep(5 * time.Second) if s.cancel { log.Println("SSH wait cancelled. Exiting loop.") return nil, errors.New("SSH wait cancelled") } // Attempt to connect to SSH port log.Printf( "Opening TCP conn for SSH to %s:%d", instance.DNSName, config.SSHPort) nc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", instance.DNSName, config.SSHPort)) if err != nil { log.Printf("TCP connection to SSH ip/port failed: %s", err) continue } // Build the actual SSH client configuration sshConfig := &gossh.ClientConfig{ User: config.SSHUsername, Auth: []gossh.ClientAuth{ gossh.ClientAuthKeyring(keyring), }, } sshConnectSuccess := make(chan bool, 1) go func() { comm, err = ssh.New(nc, sshConfig) if err != nil { log.Printf("SSH connection fail: %s", err) sshConnectSuccess <- false return } sshConnectSuccess <- true }() select { case success := <-sshConnectSuccess: if !success { continue } case <-time.After(5 * time.Second): log.Printf("SSH handshake timeout. Trying again.") continue } ui.Say("Connected via SSH!") break } // Store the connection so we can close it later s.conn = nc return comm, nil }
func (s *stepConnectSSH) Run(state map[string]interface{}) multistep.StepAction { config := state["config"].(config) instance := state["instance"].(*ec2.Instance) privateKey := state["privateKey"].(string) ui := state["ui"].(packer.Ui) // Build the keyring for authentication. This stores the private key // we'll use to authenticate. keyring := &ssh.SimpleKeychain{} err := keyring.AddPEMKey(privateKey) if err != nil { err := fmt.Errorf("Error setting up SSH config: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } // Build the actual SSH client configuration sshConfig := &gossh.ClientConfig{ User: config.SSHUsername, Auth: []gossh.ClientAuth{ gossh.ClientAuthKeyring(keyring), }, } // Start trying to connect to SSH connected := make(chan bool, 1) connectQuit := make(chan bool, 1) defer func() { connectQuit <- true }() go func() { var err error ui.Say("Connecting to the instance via SSH...") attempts := 0 for { select { case <-connectQuit: return default: } attempts += 1 log.Printf( "Opening TCP conn for SSH to %s:%d (attempt %d)", instance.DNSName, config.SSHPort, attempts) s.conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", instance.DNSName, config.SSHPort)) if err == nil { break } // A brief sleep so we're not being overly zealous attempting // to connect to the instance. time.Sleep(500 * time.Millisecond) } connected <- true }() log.Printf("Waiting up to %s for SSH connection", config.SSHTimeout) timeout := time.After(config.SSHTimeout) ConnectWaitLoop: for { select { case <-connected: // We connected. Just break the loop. break ConnectWaitLoop case <-timeout: err := errors.New("Timeout waiting for SSH to become available.") state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt case <-time.After(1 * time.Second): if _, ok := state[multistep.StateCancelled]; ok { log.Println("Interrupt detected, quitting waiting for SSH.") return multistep.ActionHalt } } } var comm packer.Communicator if err == nil { comm, err = ssh.New(s.conn, sshConfig) } if err != nil { err := fmt.Errorf("Error connecting to SSH: %s", err) state["error"] = err ui.Error(err.Error()) return multistep.ActionHalt } // Set the communicator on the state bag so it can be used later state["communicator"] = comm return multistep.ActionContinue }