// Closes a previously opened SSL connection. func (self *SSHTunnelEstablisher) tearDownSSHTunnel(config *util.Config) { if self.ciHostURL == nil { return } self.tunnelConnected.Set(false) self.resetAliveStateMonitoring(config) config.CIHostURI = self.ciHostURL.String() delete(util.JnlpArgs, "-url") delete(util.JnlpArgs, "-tunnel") if self.closables != nil && len(self.closables) > 0 { for i := len(self.closables) - 1; i >= 0; i-- { self.closables[i].Close() } self.closables = self.closables[0:0] } }
// Opens a new SSH connection, local server ports (JNLP, HTTP) and forwards it to the corresponding ports on Jenkins. func (self *SSHTunnelEstablisher) setupSSHTunnel(config *util.Config) { if self.ciHostURL == nil { return } if !config.PassCIAuth && config.SecretKey != "" { util.GOut("ssh-tunnel", "WARN: Secret key is not supported in combination with SSH tunnel. Implicitly setting %v to %v", "client>passAuth", "true") config.PassCIAuth = true } // Ensure no other SSL connections are still open. self.tearDownSSHTunnel(config) // Configuring the SSH client clientConfig := &ssh.ClientConfig{ User: config.CITunnelSSHUsername, Auth: []ssh.AuthMethod{ ssh.Password(config.CITunnelSSHPassword), }, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { expected, actual := config.CITunnelSSHFingerprint, self.formatHostFingerprint(key) if actual != expected && expected != "-" { if expected == "" { return fmt.Errorf("The host fingerprint of '%v' is '%v'. Please add this to the configuration in order to connect.", hostname, actual) } else { return fmt.Errorf("The host fingerprint of '%v' is '%v' while '%v' was expected. Connection aborted.", hostname, actual, expected) } } return nil }, } // Connecting to the SSH host if config.CITunnelSSHPort == 0 { config.CITunnelSSHPort = 22 } sshAddress := fmt.Sprintf("%v:%v", config.CITunnelSSHAddress, config.CITunnelSSHPort) sshClient, err := ssh.Dial("tcp", sshAddress, clientConfig) if err == nil { self.closables = append(self.closables, sshClient) util.GOut("ssh-tunnel", "Successfully connected with '%v'.", sshAddress) } else { util.GOut("ssh-tunnel", "ERROR: Failed connecting with %v. Cause: %v", sshAddress, err) return } // Fetching target ports jnlpTargetAddress, err := self.formatJNLPHostAndPort(config) if err != nil { util.GOut("ssh-tunnel", "ERROR: Failed fetching JNLP port from '%v'. Cause: %v.", config.CIHostURI, err) return } httpTargetAddress := self.formatHttpHostAndPort() // Creating a local server listeners to use for port forwarding. httpListener, err1 := self.newLocalServerListener() jnlpListener, err2 := self.newLocalServerListener() if err1 != nil || err2 != nil { self.tearDownSSHTunnel(config) return } // Forward local connections to the HTTP(S)/JNLP ports. go self.forwardLocalConnectionsTo(config, sshClient, httpListener, httpTargetAddress) go self.forwardLocalConnectionsTo(config, sshClient, jnlpListener, jnlpTargetAddress) // Apply the tunnel configuration localCiURL, _ := url.Parse(self.ciHostURL.String()) localCiURL.Host = httpListener.Addr().String() config.CIHostURI = localCiURL.String() util.JnlpArgs["-url"] = localCiURL.String() util.JnlpArgs["-tunnel"] = jnlpListener.Addr().String() // Mark tunnel as connected when we passed this line. self.tunnelConnected.Set(true) }