示例#1
0
文件: http.go 项目: cmikk/sshttp
func connectProxy(sshc *ssh.Client, h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "CONNECT" {
			h.ServeHTTP(w, r)
			return
		}
		host := r.URL.Host
		if strings.Index(host, ":") < 0 {
			host += ":80"
		}
		sconn, err := sshc.Dial("tcp", host)
		if err != nil {
			w.Header().Set("Content-Type", "text/plain")
			w.WriteHeader(http.StatusBadGateway)
			w.Write([]byte(err.Error()))
			return
		}

		w.WriteHeader(http.StatusOK)
		cconn, _, err := w.(http.Hijacker).Hijack()
		if err != nil {
			cconn.Close()
			sconn.Close()
			log.Print("CONNECT hijack error: ", err)
			return
		}
		go proxyconn(cconn, sconn)
		go proxyconn(sconn, cconn)
	}
}
示例#2
0
func Hop(through *ssh.Client, toaddr string, c *ssh.ClientConfig) (*ssh.Client, error) {
	hopconn, err := through.Dial("tcp", toaddr)
	if err != nil {
		return nil, err
	}

	conn, chans, reqs, err := ssh.NewClientConn(hopconn, toaddr, c)
	if err != nil {
		return nil, err
	}

	return ssh.NewClient(conn, chans, reqs), nil
}
// Forwards the local server listener to the specified target address (format host:port) using the SSH connection as tunnel.
// What this method does is the same as "ssh -L $ANY-PORT:jenkins-host:$TARGET-PORT" jenkins-host.
func (self *SSHTunnelEstablisher) forwardLocalConnectionsTo(config *util.Config, ssh *ssh.Client, listener net.Listener, targetAddress string) {
	transfer := func(source io.ReadCloser, target io.Writer) {
		defer source.Close()
		_, _ = io.Copy(target, source)
	}

	establishBIDITransport := func(source net.Conn, target net.Conn) {
		go transfer(source, target)
		go transfer(target, source)
	}

	sshAddress := ssh.Conn.RemoteAddr().String()
	localAddress := listener.Addr().String()

	util.GOut("ssh-tunnel", "Forwarding local connections on '%v' to '%v' via '%v'.", localAddress, targetAddress, sshAddress)

	for {
		if sourceConnection, err := listener.Accept(); err == nil {
			if targetConnection, err := ssh.Dial("tcp", targetAddress); err == nil {
				establishBIDITransport(sourceConnection, targetConnection)
			} else {
				util.GOut("ssh-tunnel", "ERROR: Failed forwarding incoming local connection on '%v' to '%v' via '%v'.", localAddress, targetAddress, sshAddress)
			}
		} else {
			util.GOut("ssh-tunnel", "Stop forwarding local connections on '%v' to '%v'.", localAddress, targetAddress)
			return
		}
	}
}
示例#4
0
func drainChildWaitq(waitq []chan net.Conn, address string, client *ssh.Client) ([]chan net.Conn, bool) {
	for len(waitq) > 0 {
		reply := waitq[0]
		conn, err := client.Dial("tcp", address)
		if err != nil {
			if err == io.EOF {
				// Disconnected from the SSH server.
				return waitq, true
			} else if err, ok := err.(net.Error); ok && err.Timeout() {
				log.Print(err)
				log.Printf("Timeout. Does this mean that we should recycle the connection? %v", address)
				return waitq, true
			} else {
				log.Print(err)
				log.Printf("Failed to connect to backend server at %s\n", address)
			}
		}
		reply <- conn

		waitq = waitq[1:]
	}
	return waitq, false
}
示例#5
0
func (tunnel *SSHTunnel) forward(localConn net.Conn, sshServerConn *ssh.Client) {
	/*
		serverConn, err := ssh.Dial("tcp", tunnel.Server.String(), tunnel.Config)
		if err != nil {
			Error.Fatalf("SSH Tunnel: Server dial error: %s\n", err)
			return
		}*/

	remoteConn, err := sshServerConn.Dial("tcp", tunnel.Remote.String())
	if err != nil {
		Error.Fatalf("SSH Tunnel: Remote dial error: %s\n", err)
		return
	}

	copyConn := func(writer, reader net.Conn) {
		_, err := io.Copy(writer, reader)
		if err != nil {
			Error.Fatalf("SSH Tunnel: Could not forward conenction: %s\n", err)
		}
	}

	go copyConn(localConn, remoteConn)
	go copyConn(remoteConn, localConn)
}
		go sshd.HandleConnection(serverNetConn)

		client = test_helpers.NewClient(clientNetConn, nil)
	})

	AfterEach(func() {
		client.Close()
		echoServer.Shutdown()
	})

	Context("when a session is opened", func() {
		var conn net.Conn

		JustBeforeEach(func() {
			var dialErr error
			conn, dialErr = client.Dial("tcp", echoAddress)
			Expect(dialErr).NotTo(HaveOccurred())
		})

		AfterEach(func() {
			conn.Close()
		})

		It("dials the the target from the remote end", func() {
			Expect(testDialer.DialCallCount()).To(Equal(1))

			net, addr := testDialer.DialArgsForCall(0)
			Expect(net).To(Equal("tcp"))
			Expect(addr).To(Equal(echoAddress))
		})
示例#7
0
		})

		Context("when a client requests a local port forward", func() {
			var server *ghttp.Server
			BeforeEach(func() {
				server = ghttp.NewServer()
				server.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/"),
						ghttp.RespondWith(http.StatusOK, "hi from jim\n"),
					),
				)
			})

			It("forwards the local port to the target from the server side", func() {
				lconn, err := client.Dial("tcp", server.Addr())
				Expect(err).NotTo(HaveOccurred())

				transport := &http.Transport{
					Dial: func(network, addr string) (net.Conn, error) {
						return lconn, nil
					},
				}
				client := &http.Client{Transport: transport}

				resp, err := client.Get("http://127.0.0.1/")
				Expect(err).NotTo(HaveOccurred())
				Expect(resp.StatusCode).To(Equal(http.StatusOK))

				reader := bufio.NewReader(resp.Body)
				line, err := reader.ReadString('\n')
示例#8
0
func connectSSH(info PathInfo, resp chan<- *ssh.Client, progress chan<- ProgressCmd) {
	var err error
	log.Printf("SSH-connecting to %s\n", info.SSHTunnel.Address)

	progress <- ProgressCmd{"connection_start", nil}
	sshKey := []byte(info.SSHTunnel.SSHKeyContents)
	if info.SSHTunnel.SSHKeyFileName != "" {
		sshKey, err = ioutil.ReadFile(info.SSHTunnel.SSHKeyFileName)
		if err != nil {
			progress <- ProgressCmd{"connection_failed", "Failed to read SSH key"}
			resp <- nil
			return
		}
	}

	key, err := ssh.ParsePrivateKey(sshKey)
	if err != nil {
		progress <- ProgressCmd{"connection_failed", "Failed to parse SSH key"}
		resp <- nil
		return
	}

	config := &ssh.ClientConfig{
		User: info.SSHTunnel.Username,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(key),
		},
	}

	currentRetriesServer := 0
	var sshClientConn *ssh.Client

	for {
		progress <- ProgressCmd{"connection_try", nil}
		if sshClientConn, err = dialSSH(info.SSHTunnel, config, proxyCommand); err == nil {
			break
		}

		currentRetriesServer++
		log.Printf("SSH Connection failed %s: %s\n", info.SSHTunnel.Address, err.Error())

		if currentRetriesServer < (MAX_RETRIES_SERVER / 1) {
			log.Println(`Retry...`)
			progress <- ProgressCmd{"connection_retry", nil}
			time.Sleep(1 * time.Second)
		} else {
			progress <- ProgressCmd{"connection_failed", "Connection retry limit reached"}
			resp <- nil
			return
		}
	}
	progress <- ProgressCmd{"connection_established", nil}

	runBootstrap(sshClientConn, info, progress)

	if info.SSHTunnel.Run != nil {
		session, _ := sshClientConn.NewSession()

		modes := ssh.TerminalModes{
			ssh.ECHO: 0,
		}

		if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
			log.Fatalf("request for pseudo terminal failed: %s", err)
		}

		session.Start(info.SSHTunnel.Run.Command)
		time.Sleep(500 * time.Millisecond)
	}
	log.Printf("SSH-connection OK. Waiting for %s to be ready...\n", info.Backend.Address)

	progress <- ProgressCmd{"waiting_backend", nil}
	currentRetriesClient := 0
	for {
		if conn, err := sshClientConn.Dial("tcp", info.Backend.Address); err == nil {
			conn.Close()
			break
		}
		currentRetriesClient++

		if currentRetriesClient < (MAX_RETRIES_CLIENT / 5) {
			log.Println(`Retry...`)
			progress <- ProgressCmd{"waiting_backend_retry", nil}
			time.Sleep(5 * time.Second)
		} else {
			progress <- ProgressCmd{"waiting_backend_timeout", "Connection retry limit reached"}
			resp <- nil
			return
		}
	}

	progress <- ProgressCmd{"connection_success", nil}
	resp <- sshClientConn
}
		})

		AfterEach(func() {
			client.Close()
		})

		Context("when a client requests the execution of a command", func() {
			It("runs the command", func() {
				_, err := client.NewSession()
				Expect(err).To(MatchError(ContainSubstring("not supported")))
			})
		})

		Context("when a client requests a local port forward", func() {
			var server *ghttp.Server
			BeforeEach(func() {
				server = ghttp.NewServer()
			})

			It("forwards the local port to the target from the server side", func() {
				_, err := client.Dial("tcp", server.Addr())
				Expect(err).To(MatchError(ContainSubstring("unknown channel type")))
			})

			It("server should not receive any connections", func() {
				Expect(server.ReceivedRequests()).To(BeEmpty())
			})
		})
	})
})
示例#10
0
func forward(localConn net.Conn, config *ssh.ClientConfig, serverAddrString, remoteAddrString string) {

	defer localConn.Close()
	currentRetriesServer := 0
	currentRetriesRemote := 0
	var sshClientConnection *ssh.Client = nil

	// Loop for retries:
	for {

		// Try to connect to the SSH server:
		if sshClientConn, err := ssh.Dial(`tcp`, serverAddrString, config); err != nil {

			// Failed:
			currentRetriesServer++
			log.Printf("Was not able to connect with the SSH server %s: %s\n", serverAddrString, err.Error())

			// Is a retry alowed?
			if currentRetriesServer < maxRetriesServer {
				log.Println(`Retry...`)
				time.Sleep(1 * time.Second)
			} else {

				// After the return, this thread is closed down. The client can try it again...
				log.Println(`No more retries for connecting the SSH server.`)
				return
			}

		} else {

			// Success:
			log.Println(`Connected to the SSH server ` + serverAddrString)
			sshClientConnection = sshClientConn
			defer sshClientConnection.Close()
			break
		}
	}

	// Loop for retries:
	for {

		// Try to create the remote end-point:
		if sshConn, err := sshClientConnection.Dial(`tcp`, remoteAddrString); err != nil {

			// Failed:
			currentRetriesRemote++
			log.Printf("Was not able to create the remote end-point %s: %s\n", remoteAddrString, err.Error())

			// Is another retry allowed?
			if currentRetriesRemote < maxRetriesRemote {
				log.Println(`Retry...`)
				time.Sleep(1 * time.Second)
			} else {

				// After the return, this thread is closed down. The client can try it again...
				log.Println(`No more retries for connecting the remote end-point.`)
				return
			}
		} else {

			// Fine, the connections are up and ready :-)
			log.Printf("The remote end-point %s is connected.\n", remoteAddrString)
			defer sshConn.Close()

			// To be able to close down both transfer threads, we create a channel:
			quit := make(chan bool)

			// Create the transfers to/from both sides (two new threads are created for this):
			go transfer(localConn, sshConn, `Local => Remote`, quit)
			go transfer(sshConn, localConn, `Remote => Local`, quit)

			// Wait and look if any of the two transfer theads are down:
			isRunning := true
			for isRunning {
				select {
				case <-quit:
					log.Println(`At least one transfer was stopped.`)
					isRunning = false
					break
				}
			}

			// Now, close all the channels and therefore, force the other / second thread to go down:
			log.Println(`Close now all connections.`)
			return
		}
	}
}