Exemple #1
1
func (ctrl *Controller) terminal(ctx scope.Context, ch ssh.Channel) {
	defer ch.Close()

	lines := make(chan string)
	term := terminal.NewTerminal(ch, "> ")

	go func() {
		for ctx.Err() == nil {
			line, err := term.ReadLine()
			if err != nil {
				ctx.Terminate(err)
				return
			}
			lines <- line
		}
	}()

	for {
		var line string
		select {
		case <-ctx.Done():
			return
		case line = <-lines:
		}

		cmd := parse(line)
		fmt.Printf("[control] > %v\n", cmd)
		switch cmd[0] {
		case "":
			continue
		case "quit":
			return
		case "shutdown":
			ctrl.ctx.Terminate(fmt.Errorf("shutdown initiated from console"))
		default:
			runCommand(ctx.Fork(), ctrl, cmd[0], term, cmd[1:])
		}
	}
}
func pipeRequests(psChannel, sChannel ssh.Channel, psRequests, sRequests <-chan *ssh.Request) {
	defer func() {
		return
		if r := recover(); r != nil {
			fmt.Println("Recovered in f", r)
		}
	}()
	defer sChannel.Close()
	defer psChannel.Close()

	for {
		select {
		case lRequest, ok := <-psRequests:
			if !ok {
				return
			}
			if err := forwardRequest(lRequest, sChannel); err != nil {
				fmt.Println("Error: " + err.Error())
				continue
			}
		case rRequest, ok := <-sRequests:
			if !ok {
				return
			}
			if err := forwardRequest(rRequest, psChannel); err != nil {
				fmt.Println("Error: " + err.Error())
				continue
			}
		}
	}
}
Exemple #3
0
func ProxyRequests(logger lager.Logger, channelType string, reqs <-chan *ssh.Request, channel ssh.Channel) {
	logger = logger.Session("proxy-requests", lager.Data{
		"channel-type": channelType,
	})

	logger.Info("started")
	defer logger.Info("completed")
	defer channel.Close()

	for req := range reqs {
		logger.Info("request", lager.Data{
			"type":      req.Type,
			"wantReply": req.WantReply,
			"payload":   req.Payload,
		})
		success, err := channel.SendRequest(req.Type, req.WantReply, req.Payload)
		if err != nil {
			logger.Error("send-request-failed", err)
			continue
		}

		if req.WantReply {
			req.Reply(success, nil)
		}
	}
}
Exemple #4
0
func handleSessionRequests(logger log.Logger, channel ssh.Channel, requests <-chan *ssh.Request, system datamodel.System, user datamodel.User) {
	defer channel.Close()

	// Sessions have out-of-band requests such as "shell",
	// "pty-req" and "env".  Here we handle only the
	// "shell" request.
	for req := range requests {

		ok := false
		switch req.Type {
		case "shell":
			ok = true

			if len(req.Payload) > 0 {
				fmt.Println(string(req.Payload))
				// We don't accept any
				// commands, only the
				// default shell.
				ok = false
			}

		case "pty-req":
			// Responding 'ok' here will let the client
			// know we have a pty ready for input
			ok = true

			go startTerminal(logger, channel, system, user)
		default:
			// fmt.Println("default req: ", req)
		}

		req.Reply(ok, nil)
	}
}
Exemple #5
0
func (s *shellHandler) startTerminal(parentTomb tomb.Tomb, sshConn *ssh.ServerConn, channel ssh.Channel) error {
	defer channel.Close()

	prompt := ">>> "
	term := terminal.NewTerminal(channel, prompt)

	// // Try to make the terminal raw
	// oldState, err := terminal.MakeRaw(0)
	// if err != nil {
	//     logger.Warn("Error making terminal raw: ", err.Error())
	// }
	// defer terminal.Restore(0, oldState)

	// Get username
	username, ok := sshConn.Permissions.Extensions["username"]
	if !ok {
		username = "******"
	}

	// Write ascii text
	term.Write([]byte(fmt.Sprintf("\r\n Nice job, %s! You are connected!\r\n", username)))
	defer term.Write([]byte(fmt.Sprintf("\r\nGoodbye, %s!\r\n", username)))

	// Start REPL
	for {

		select {
		case <-parentTomb.Dying():
			return nil
		default:
			s.logger.Info("Reading line...")
			input, err := term.ReadLine()
			if err != nil {
				fmt.Errorf("Readline() error")
				return err
			}

			// Process line
			line := strings.TrimSpace(input)
			if len(line) > 0 {

				// Log input and handle exit requests
				if line == "exit" || line == "quit" {
					s.logger.Info("Closing connection")
					return nil
				}

				// Echo input
				channel.Write(term.Escape.Green)
				channel.Write([]byte(line + "\r\n"))
				channel.Write(term.Escape.Reset)
			}
		}
	}
	return nil
}
func (s *sshServer) handleRequests(channel ssh.Channel, in <-chan *ssh.Request) error {
	env := make(map[string]string)

	for req := range in {
		switch req.Type {
		default:
			log.Printf("unrecognized ssh request type=%q payload=%s wantreply=%t", req.Type, req.Payload, req.WantReply)
			req.Reply(false, nil) // unhandled; tell them so

		case "env":
			var e envReq
			if err := ssh.Unmarshal(req.Payload, &e); err != nil {
				req.Reply(false, nil)
				return err
			}
			req.Reply(true, nil)
			env[string(e.Key)] = string(e.Val)

		case "exec":
			var e execReq
			if err := ssh.Unmarshal(req.Payload, &e); err != nil {
				req.Reply(false, nil)
				return err
			}
			req.Reply(true, nil)

			var cmdbuf bytes.Buffer
			for k, v := range env {
				cmdbuf.WriteString(k)
				cmdbuf.WriteByte('=')
				cmdbuf.WriteString(v)
				cmdbuf.WriteByte(' ')
			}
			cmdbuf.Write(e.Command)

			log.Printf("Running command %q", cmdbuf.String())
			cmd := &packer.RemoteCmd{Command: cmdbuf.String()}
			cmd.Stdout = channel
			cmd.Stderr = channel.Stderr()
			var rc int
			if err := s.comm.Start(cmd); err != nil {
				rc = 255 // TODO: What is a better choice here?
			} else {
				cmd.Wait()
				rc = cmd.ExitStatus
			}
			channel.CloseWrite()
			channel.SendRequest("exit-status", false, []byte{0, 0, 0, byte(rc)})
			channel.Close()
		}
	}

	return nil
}
Exemple #7
0
func (s *shellHandler) Handle(parentTomb tomb.Tomb, sshConn *ssh.ServerConn, channel ssh.Channel, requests <-chan *ssh.Request) error {
	defer channel.Close()

	users, err := s.system.Users()
	if err != nil {
		return err
	}

	user, err := users.Get(sshConn.Permissions.Extensions["username"])
	if err != nil {
		return err
	}

	// Create tomb for terminal goroutines
	var t tomb.Tomb

	// Sessions have out-of-band requests such as "shell",
	// "pty-req" and "env".  Here we handle only the
	// "shell" request.
	for {
		select {
		case <-parentTomb.Dying():
			t.Kill(nil)
			return t.Wait()
		case req := <-requests:

			ok := false
			switch req.Type {
			case "shell":
				ok = true

				if len(req.Payload) > 0 {
					fmt.Println(string(req.Payload))
					// We don't accept any
					// commands, only the
					// default shell.
					ok = false
				}

			case "pty-req":
				// Responding 'ok' here will let the client
				// know we have a pty ready for input
				ok = true

				go s.startTerminal(t, channel, s.system, user)
			default:
				// fmt.Println("default req: ", req)
			}

			req.Reply(ok, nil)
		}
	}
	return nil
}
Exemple #8
0
func (s *shellHandler) Handle(parentTomb tomb.Tomb, sshConn *ssh.ServerConn, channel ssh.Channel, requests <-chan *ssh.Request) error {
	defer channel.Close()
	s.logger.Info("WooHoo!!! Inside Handler!")

	// Create tomb for terminal goroutines
	var t tomb.Tomb

	// Sessions have out-of-band requests such as "shell",
	// "pty-req" and "env".  Here we handle only the
	// "shell" request.
	t.Go(func() error {
	OUTER:
		for {
			select {
			case <-parentTomb.Dying():
				t.Kill(nil)
				break OUTER
			case req := <-requests:
				if req == nil {
					break OUTER
				}

				ok := false
				switch req.Type {
				case "shell":
					ok = true

					if len(req.Payload) > 0 {
						// fmt.Println(string(req.Payload))

						// We don't accept any
						// commands, only the
						// default shell.
						ok = false
					}

				case "pty-req":
					// Responding 'ok' here will let the client
					// know we have a pty ready for input
					ok = true

					t.Go(func() error {
						return s.startTerminal(t, sshConn, channel)
					})
				}

				req.Reply(ok, nil)
			}
		}
		return nil
	})
	return t.Wait()
}
Exemple #9
0
func handleChannelRequests(logger log.Logger, channel ssh.Channel, requests <-chan *ssh.Request, system datamodel.System, user datamodel.User) {
	defer channel.Close()

	for req := range requests {
		if req.Type == "skl" {
			logger.Info("SKL request", "request", string(req.Payload))
			req.Reply(true, nil)
		} else {
			if req.WantReply {
				req.Reply(false, nil)
			}
		}
	}
}
Exemple #10
0
func proxy(reqs1, reqs2 <-chan *ssh.Request, channel1, channel2 ssh.Channel) {
	var closer sync.Once
	closeFunc := func() {
		channel1.Close()
		channel2.Close()
	}

	defer closer.Do(closeFunc)

	closerChan := make(chan bool, 1)

	go func() {
		io.Copy(channel1, channel2)
		closerChan <- true
	}()

	go func() {
		io.Copy(channel2, channel1)
		closerChan <- true
	}()

	for {
		select {
		case req := <-reqs1:
			if req == nil {
				return
			}
			b, err := channel2.SendRequest(req.Type, req.WantReply, req.Payload)
			if err != nil {
				return
			}
			req.Reply(b, nil)

		case req := <-reqs2:
			if req == nil {
				return
			}
			b, err := channel1.SendRequest(req.Type, req.WantReply, req.Payload)
			if err != nil {
				return
			}
			req.Reply(b, nil)
		case <-closerChan:
			return
		}
	}
}
Exemple #11
0
func (sshd *SSHD) handleChannel(channel ssh.Channel, requests <-chan *ssh.Request, container string) {
	cmd := exec.Command("docker", "exec", "-i", "-t", container, "/bin/bash")
	closeChannel := func() {
		channel.Close()
		_, err := cmd.Process.Wait()
		if err != nil {
			log.Errorf("failed to exit docker exec (%s)", err)
		}
	}

	fp, err := pty.Start(cmd)
	if err != nil {
		log.Error("pty.Start: ", err)
		closeChannel()
		return
	}

	go func() {
		for req := range requests {
			log.Debugf("new request: %s", req.Type)
			switch req.Type {
			case "shell":
				if len(req.Payload) == 0 {
					req.Reply(true, nil)
				}
			case "pty-req":
				termLen := req.Payload[3]
				w, h := sshd.parseDims(req.Payload[termLen+4:])
				sshd.setWinsize(fp.Fd(), w, h)
				req.Reply(true, nil)
			case "window-change":
				w, h := sshd.parseDims(req.Payload)
				sshd.setWinsize(fp.Fd(), w, h)
			case "env":
			}
		}
	}()

	var once sync.Once
	cp := func(dst io.Writer, src io.Reader) {
		io.Copy(dst, src)
		once.Do(closeChannel)
	}
	go cp(channel, fp)
	go cp(fp, channel)
}
// Payload: int: command size, string: command
func handleExec(ch ssh.Channel, req *ssh.Request) {
	command := string(req.Payload[4:])
	gitCmds := []string{"git-receive-pack", "git-upload-pack"}

	valid := false
	for _, cmd := range gitCmds {
		if strings.HasPrefix(command, cmd) {
			valid = true
		}
	}
	if !valid {
		ch.Write([]byte("command is not a GIT command\r\n"))
		ch.Close()
		return
	}

	ch.Write([]byte("well done!\r\n"))
	ch.Close()
}
Exemple #13
0
func handleChannel(ch ssh.Channel) {
	term := terminal.NewTerminal(ch, "> ")
	serverTerm := &ssh.ServerTerminal{
		Term:    term,
		Channel: ch,
	}
	ch.Accept()
	defer ch.Close()
	for {
		line, err := serverTerm.ReadLine()
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Println("handleChannel readLine err:", err)
			continue
		}
		fmt.Println(line)
	}
}
Exemple #14
0
/* handleChannelRequests handles proxying requests read from reqs to the SSH
channel c.  info is used for logging. */
func handleChannelRequests(
	reqs <-chan *ssh.Request,
	c ssh.Channel,
	info string,
) {
	for r := range reqs {
		go handleRequest(
			r,
			func( /* Ugh, a closure */
				name string,
				wantReply bool,
				payload []byte,
			) (bool, []byte, error) {
				b, e := c.SendRequest(name, wantReply, payload)
				return b, nil, e
			},
			func() error { return c.Close() }, /* Another? */
			info,
		)
	}
}
Exemple #15
0
// Payload: int: command size, string: command
func handleExec(ch ssh.Channel, req *ssh.Request) {
	command := string(req.Payload[4:])

	client, session, err := connectUpstream()
	if err != nil {
		ch.Write([]byte("fail to connect upstream: " + err.Error() + "\r\n"))
		ch.Close()
		return
	}

	exitStatus, err := pipe(ch, client, session, command)
	if err != nil {
		ch.Write([]byte("fail to pipe command:" + err.Error()))
		ch.Close()
		return
	}

	exitStatusBuffer := make([]byte, 4)
	binary.PutUvarint(exitStatusBuffer, uint64(exitStatus))
	log.Println("forward exit-code", exitStatus, "to client")
	_, err = ch.SendRequest("exit-status", false, exitStatusBuffer)
	if err != nil {
		log.Println("Failed to forward exit-status to client:", err)
	}

	ch.Close()
	client.Close()
	log.Println("End of exec")
}
Exemple #16
0
// This executes the commands requested to be run on the server.
// Used to test the SSH secret backend.
func executeServerCommand(ch ssh.Channel, req *ssh.Request) {
	command := string(req.Payload[4:])
	cmd := exec.Command("/bin/bash", []string{"-c", command}...)
	req.Reply(true, nil)

	cmd.Stdout = ch
	cmd.Stderr = ch
	cmd.Stdin = ch

	err := cmd.Start()
	if err != nil {
		panic(fmt.Sprintf("Error starting the command: '%s'", err))
	}

	go func() {
		_, err := cmd.Process.Wait()
		if err != nil {
			panic(fmt.Sprintf("Error while waiting for command to finish:'%s'", err))
		}
		ch.Close()
	}()
}
func (s *SSHServer) execHandler(channel ssh.Channel, request *ssh.Request) error {
	defer channel.Close()

	var payload = struct {
		Value string
	}{}
	if err := ssh.Unmarshal(request.Payload, &payload); err != nil {
		return fmt.Errorf("failed to unmarshal payload: %v", err)
	}

	result, status, err := s.runCmd(payload.Value)
	if err != nil {
		return fmt.Errorf("failed to run command: %v", err)
	}
	if err := sendCmdResult(channel, result, status); err != nil {
		return fmt.Errorf("failed to send result: %v", err)
	}
	if err := request.Reply(true, nil); err != nil {
		return fmt.Errorf("failed to send reply: %v", err)
	}
	return nil
}
Exemple #18
0
func (server *Server) handleRequest(channel ssh.Channel, requests <-chan *ssh.Request, conn *ssh.ServerConn) {
	defer func() {
		err := channel.Close()
		if err != nil {
			server.Logger.Errorf("Failed to close SSH Channel from %s due to %s",
				conn.RemoteAddr().String(), err)
		}
		server.Logger.Debugf("Close SSH Channel from %s", conn.RemoteAddr().String())
	}()
	for req := range requests {
		server.Logger.Debugf("Received new SSH Request (type = %s) from %s", req.Type, conn.RemoteAddr().String())

		switch req.Type {
		case "exec":
			server.handleExecRequest(channel, req, conn)
		default:
			var err error
			if req.Type == "env" {
				_, err = channel.Stderr().Write([]byte("error: Pages does not support SendEnv.\n"))
			} else {
				_, err = channel.Write([]byte("You've successfully authenticated, but Pages does not provide shell access.\n"))
			}
			if err != nil && err != io.EOF {
				server.Logger.Errorf("Failed to Talk to SSH Request due to %s", err)
			}
			err = req.Reply(false, nil)
			if err != nil && err != io.EOF {
				server.Logger.Errorf("Failed to Reply false to SSH Request due to %s", err)
			}
			err = channel.Close()
			if err != nil && err != io.EOF {
				server.Logger.Errorf("Failed to close SSH Request due to %s", err)
			}
			server.Logger.Errorf("Close SSH Request due to unsupported SSH Request type: %s", req.Type)
		}
		return
	}
}
func (s *SSHServer) shellHandler(channel ssh.Channel) error {
	defer channel.Close()

	term := terminal.NewTerminal(channel, "# ")
	for {
		line, err := term.ReadLine()
		if err != nil {
			break
		}
		if line == "" {
			return nil
		}

		result, status, err := s.runCmd(line)
		if err != nil {
			return fmt.Errorf("failed to run command: %v", err)
		}
		if err := sendCmdResult(channel, result, status); err != nil {
			return fmt.Errorf("failed to send result: %v", err)
		}
	}
	return nil
}
Exemple #20
0
func forwardUnixSocket(channel ssh.Channel, addr string) {
	conn, err := net.Dial("unix", addr)
	if err != nil {
		return
	}

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		io.Copy(conn, channel)
		conn.(*net.UnixConn).CloseWrite()
		wg.Done()
	}()
	go func() {
		io.Copy(channel, conn)
		channel.CloseWrite()
		wg.Done()
	}()

	wg.Wait()
	conn.Close()
	channel.Close()
}
Exemple #21
0
func (s *server) handleExec(ch ssh.Channel, req *ssh.Request, authInfo map[string]string) {
	defer ch.Close()
	args := strings.SplitN(string(req.Payload[4:]), " ", 2) //remove the 4 bytes of git protocol indicating line length
	command := args[0]
	repo := strings.TrimSuffix(strings.TrimPrefix(args[1], "'/"), ".git'")

	//auth the user
	if os.Getenv("GITHUB_AUTH") == "true" {
		gauth, err := auth.NewGithubAuth()
		if err != nil {
			writePktLine(fmt.Sprintf("github auth error, contact an administrator: %s", err), ch)
			return
		}

		if err := gauth.Authenticate(authInfo["user"], authInfo["public_key"], repo); err != nil {
			writePktLine(fmt.Sprintf("github auth failed: %s", err), ch)
			return
		}
	}

	//check if allowed command
	allowed := []string{pullCmd, pushCmd}
	ok := false
	for _, c := range allowed {
		if command == c {
			ok = true
			break
		}
	}

	if !ok {
		log.Infof("command %s not allowed on this server", command)
		writePktLine(fmt.Sprintf("%s not allowed on this server", command), ch)
		return
	}

	log.Infof("receiving %s command for repo %s", command, repo)

	repoPath, err := s.prepareRepo(repo)
	if err != nil {
		log.Errorf("unable to create repo: %v", err)
		writePktLine(err.Error(), ch)
		return
	}

	defer func() {
		if err := s.unlockRepo(repo); err != nil {
			log.Errorf("unable to unlock repo: %v", err)
			writePktLine(err.Error(), ch)
		}
	}()

	cmd := exec.Command(command, repoPath)
	wg, err := attachCmd(cmd, ch)
	if err != nil {
		log.Errorf("unable to attach command stdio: %v", err)
		writePktLine(err.Error(), ch)
		return
	}

	if err := cmd.Start(); err != nil {
		log.Errorf("unable to start command: %v", err)
		writePktLine(err.Error(), ch)
		return
	}
	wg.Wait()
	syscallErr := cmd.Wait()

	ch.SendRequest("exit-status", false, ssh.Marshal(exitStatus(syscallErr)))
}
	Context("when a session channel is opened", func() {
		var channel ssh.Channel
		var requests <-chan *ssh.Request

		BeforeEach(func() {
			var err error
			channel, requests, err = client.OpenChannel("session", nil)
			Expect(err).NotTo(HaveOccurred())

			go ssh.DiscardRequests(requests)
		})

		AfterEach(func() {
			if channel != nil {
				channel.Close()
			}
		})

		Context("and an exec request fails to unmarshal", func() {
			It("rejects the request", func() {
				accepted, err := channel.SendRequest("exec", true, ssh.Marshal(struct{ Bogus uint32 }{Bogus: 1138}))
				Expect(err).NotTo(HaveOccurred())
				Expect(accepted).To(BeFalse())
			})
		})

		Context("and an env request fails to unmarshal", func() {
			It("rejects the request", func() {
				accepted, err := channel.SendRequest("env", true, ssh.Marshal(struct{ Bogus int }{Bogus: 1234}))
				Expect(err).NotTo(HaveOccurred())
Exemple #23
0
// answer handles answering requests and channel requests
//
// Currently, an exec must be either "ping", "git-receive-pack" or
// "git-upload-pack". Anything else will result in a failure response. Right
// now, we leave the channel open on failure because it is unclear what the
// correct behavior for a failed exec is.
//
// Support for setting environment variables via `env` has been disabled.
func (s *server) answer(channel ssh.Channel, requests <-chan *ssh.Request, condata string, sshconn *ssh.ServerConn) error {
	defer channel.Close()

	// Answer all the requests on this connection.
	for req := range requests {
		ok := false

		switch req.Type {
		case "env":
			o := &EnvVar{}
			ssh.Unmarshal(req.Payload, o)
			log.Info("Key='%s', Value='%s'\n", o.Name, o.Value)
			req.Reply(true, nil)
		case "exec":
			clean := cleanExec(req.Payload)
			parts := strings.SplitN(clean, " ", 2)
			switch parts[0] {
			case "ping":
				err := Ping(channel, req)
				if err != nil {
					log.Info("Error pinging: %s", err)
				}
				return err
			case "git-receive-pack", "git-upload-pack":
				if len(parts) < 2 {
					log.Info("Expected two-part command.")
					req.Reply(ok, nil)
					break
				}
				repoName, err := cleanRepoName(parts[1])
				if err != nil {
					log.Err("Illegal repo name: %s.", err)
					channel.Stderr().Write([]byte("No repo given"))
					return err
				}
				wrapErr := wrapInLock(s.pushLock, repoName, time.Duration(0), s.runReceive(req, sshconn, channel, repoName, parts, condata))
				if wrapErr == errAlreadyLocked {
					log.Info(multiplePush)
					// The error must be in git format
					if pktErr := gitPktLine(channel, fmt.Sprintf("ERR %v\n", multiplePush)); pktErr != nil {
						log.Err("Failed to write to channel: %s", err)
					}
					sendExitStatus(1, channel)
					req.Reply(false, nil)
					return nil
				}

				var xs uint32
				if wrapErr != nil {
					log.Err("Failed git receive: %v", err)
					xs = 1
				}
				sendExitStatus(xs, channel)

				return nil
			default:
				log.Info("Illegal command is '%s'\n", clean)
				req.Reply(false, nil)
				return nil
			}

			if err := sendExitStatus(0, channel); err != nil {
				log.Err("Failed to write exit status: %s", err)
			}
			return nil
		default:
			// We simply ignore all of the other cases and leave the
			// channel open to take additional requests.
			log.Info("Received request of type %s\n", req.Type)
			req.Reply(false, nil)
		}
	}

	return nil
}
Exemple #24
0
func (BadHandler) Handle(t tomb.Tomb, conn *ssh.ServerConn, channel ssh.Channel, requests <-chan *ssh.Request) error {
	defer channel.Close()
	return fmt.Errorf("An error occurred")
}
Exemple #25
0
func (e *EchoHandler) Handle(t tomb.Tomb, conn *ssh.ServerConn, channel ssh.Channel, requests <-chan *ssh.Request) error {
	defer channel.Close()
	e.logger.Info("echo handle called!")

	// Create tomb for terminal goroutines
	var tmb tomb.Tomb

	type msg struct {
		line     []byte
		isPrefix bool
		err      error
	}

	in := make(chan msg)
	defer close(in)
	reader := bufio.NewReader(channel)
	tmb.Go(func() error {
		tmb.Go(func() error {
			for {
				line, pre, err := reader.ReadLine()
				if err != nil {
					tmb.Kill(nil)
					return nil
				}

				select {
				case in <- msg{line, pre, err}:
				case <-t.Dying():
					tmb.Kill(nil)
					return nil
				case <-tmb.Dying():
					return nil
				}
			}
		})

		tmb.Go(func() error {
			for {
				e.logger.Info("time: ", time.Now())
				select {
				case <-tmb.Dying():
					return nil
				case <-t.Dying():
					tmb.Kill(nil)
					return nil
				case m := <-in:
					if m.err != nil {
						tmb.Kill(m.err)
						return m.err
					}

					// Send echo
					channel.Write(m.line)
				}
			}
		})
		return nil
	})

	return tmb.Wait()
}
Exemple #26
0
// answer handles answering requests and channel requests
//
// Currently, an exec must be either "ping", "git-receive-pack" or
// "git-upload-pack". Anything else will result in a failure response. Right
// now, we leave the channel open on failure because it is unclear what the
// correct behavior for a failed exec is.
//
// Support for setting environment variables via `env` has been disabled.
func (s *server) answer(channel ssh.Channel, requests <-chan *ssh.Request, sshConn string) error {
	defer channel.Close()

	// Answer all the requests on this connection.
	for req := range requests {
		ok := false

		// I think that ideally what we want to do here is pass this on to
		// the Cookoo router and let it handle each Type on its own.
		switch req.Type {
		case "env":
			o := &EnvVar{}
			ssh.Unmarshal(req.Payload, o)
			fmt.Printf("Key='%s', Value='%s'\n", o.Name, o.Value)
			req.Reply(true, nil)
		case "exec":
			clean := cleanExec(req.Payload)
			parts := strings.SplitN(clean, " ", 2)

			router := s.c.Get("cookoo.Router", nil).(*cookoo.Router)

			// TODO: Should we unset the context value 'cookoo.Router'?
			// We need a shallow copy of the context to avoid race conditions.
			cxt := s.c.Copy()
			cxt.Put("SSH_CONNECTION", sshConn)

			// Only allow commands that we know about.
			switch parts[0] {
			case "ping":
				cxt.Put("channel", channel)
				cxt.Put("request", req)
				sshPing := cxt.Get("route.sshd.sshPing", "sshPing").(string)
				err := router.HandleRequest(sshPing, cxt, true)
				if err != nil {
					log.Warnf(s.c, "Error pinging: %s", err)
				}
				return err
			case "git-receive-pack", "git-upload-pack":
				if len(parts) < 2 {
					log.Warn(s.c, "Expected two-part command.\n")
					req.Reply(ok, nil)
					break
				}
				req.Reply(true, nil) // We processed. Yay.

				cxt.Put("channel", channel)
				cxt.Put("request", req)
				cxt.Put("operation", parts[0])
				cxt.Put("repository", parts[1])
				sshGitReceive := cxt.Get("route.sshd.sshGitReceive", "sshGitReceive").(string)
				err := router.HandleRequest(sshGitReceive, cxt, true)
				var xs uint32
				if err != nil {
					log.Errf(s.c, "Failed git receive: %v", err)
					xs = 1
				}
				sendExitStatus(xs, channel)
				return nil
			default:
				log.Warnf(s.c, "Illegal command is '%s'\n", clean)
				req.Reply(false, nil)
				return nil
			}

			if err := sendExitStatus(0, channel); err != nil {
				log.Errf(s.c, "Failed to write exit status: %s", err)
			}
			return nil
		default:
			// We simply ignore all of the other cases and leave the
			// channel open to take additional requests.
			log.Infof(s.c, "Received request of type %s\n", req.Type)
			req.Reply(false, nil)
		}
	}

	return nil
}
Exemple #27
0
func (c *Client) runCommand(channel ssh.Channel, entrypoint string, command []string) {
	var cmd *exec.Cmd
	var err error

	if c.Config.IsLocal {
		cmd = exec.Command(entrypoint, command...)
	} else {
		// checking if a container already exists for this user
		existingContainer := ""
		if !c.Server.NoJoin {
			cmd = exec.Command("docker", "ps", "--filter=label=ssh2docker", fmt.Sprintf("--filter=label=image=%s", c.Config.ImageName), fmt.Sprintf("--filter=label=user=%s", c.Config.RemoteUser), "--quiet", "--no-trunc")
			cmd.Env = c.Config.Env.List()
			buf, err := cmd.CombinedOutput()
			if err != nil {
				log.Warnf("docker ps ... failed: %v", err)
				channel.Close()
				return
			}
			existingContainer = strings.TrimSpace(string(buf))
		}

		// Opening Docker process
		if existingContainer != "" {
			// Attaching to an existing container
			args := []string{"exec"}
			if len(c.Config.DockerExecArgs) > 0 {
				args = append(args, c.Config.DockerExecArgs...)
				if err := c.alterArgs(args); err != nil {
					log.Errorf("Failed to execute template on args: %v", err)
					return
				}
			} else {
				inlineExec, err := c.alterArg(c.Server.DockerExecArgsInline)
				if err != nil {
					log.Errorf("Failed to execute template on arg: %v", err)
					return
				}
				execArgs, err := shlex.Split(inlineExec)
				if err != nil {
					log.Errorf("Failed to split arg %q: %v", inlineExec, err)
					return
				}
				args = append(args, execArgs...)
			}

			args = append(args, existingContainer)
			if entrypoint != "" {
				args = append(args, entrypoint)
			}
			args = append(args, command...)
			log.Debugf("Executing 'docker %s'", strings.Join(args, " "))
			cmd = exec.Command("docker", args...)
			cmd.Env = c.Config.Env.List()
		} else {
			// Creating and attaching to a new container
			args := []string{"run"}
			if len(c.Config.DockerRunArgs) > 0 {
				args = append(args, c.Config.DockerRunArgs...)
				if err := c.alterArgs(args); err != nil {
					log.Errorf("Failed to execute template on args: %v", err)
					return
				}
			} else {
				inlineRun, err := c.alterArg(c.Server.DockerRunArgsInline)
				if err != nil {
					log.Errorf("Failed to execute template on arg: %v", err)
					return
				}
				runArgs, err := shlex.Split(inlineRun)
				if err != nil {
					log.Errorf("Failed to split arg %q: %v", inlineRun, err)
					return
				}
				args = append(args, runArgs...)
			}

			args = append(args, "--label=ssh2docker", fmt.Sprintf("--label=user=%s", c.Config.RemoteUser), fmt.Sprintf("--label=image=%s", c.Config.ImageName))
			if c.Config.User != "" {
				args = append(args, "-u", c.Config.User)
			}
			if entrypoint != "" {
				args = append(args, "--entrypoint", entrypoint)
			}

			args = append(args, c.Config.ImageName)
			args = append(args, command...)
			log.Debugf("Executing 'docker %s'", strings.Join(args, " "))
			cmd = exec.Command("docker", args...)
			cmd.Env = c.Config.Env.List()
		}
	}

	if c.Server.Banner != "" {
		banner := c.Server.Banner
		banner = strings.Replace(banner, "\r", "", -1)
		banner = strings.Replace(banner, "\n", "\n\r", -1)
		fmt.Fprintf(channel, "%s\n\r", banner)
	}

	cmd.Stdout = channel
	cmd.Stdin = channel
	cmd.Stderr = channel
	var wg sync.WaitGroup
	if c.Config.UseTTY {
		cmd.Stdout = c.Tty
		cmd.Stdin = c.Tty
		cmd.Stderr = c.Tty

		wg.Add(1)
		go func() {
			io.Copy(channel, c.Pty)
			wg.Done()
		}()
		wg.Add(1)
		go func() {
			io.Copy(c.Pty, channel)
			wg.Done()
		}()
		defer wg.Wait()
	}
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setctty: c.Config.UseTTY,
		Setsid:  true,
	}

	err = cmd.Start()
	if err != nil {
		log.Warnf("cmd.Start failed: %v", err)
		channel.Close()
		return
	}

	if err := cmd.Wait(); err != nil {
		log.Warnf("cmd.Wait failed: %v", err)
	}
	channel.Close()
	log.Debugf("cmd.Wait done")
}
Exemple #28
0
func prepHandle(channel ssh.Channel, requests <-chan *ssh.Request, handler Handler) error {
	if channel == nil {
		return fmt.Errorf("No session channel received.\n")
	}
	defer channel.Close()

	var termtype string
	var width, height uint32
	var shell = false
	var pty = false
	after := time.After(time.Second * 3)
	for !(shell && pty) {
		select {
		case request, ok := <-requests:
			if !ok {
				return nil
			}
			fmt.Printf("request: %s\n", request.Type)
			switch request.Type {
			case "shell":
				// TODO: Maybe only allow zero-paylength shells (no command)
				// if len(request.Payload) == 0
				shell = true
				request.Reply(true, nil)
			case "pty-req":
				_, termtype, width, height = requestPtyReq(request)
				pty = true
				request.Reply(true, nil)
			default:
				request.Reply(false, nil)
			}
		case <-after:
			return fmt.Errorf("No shell and pty request after 3 seconds")
		}
	}

	t := term.New(term.Resolve(termtype), channel)
	if t == nil {
		return fmt.Errorf("Unknown terminal type '%s'", termtype)
	}
	t.SetDimensions(width, height)

	windowsize := make(chan WindowSize)

	go func() {
		for request := range requests {
			switch request.Type {
			case "window-change":
				_, width, height := requestWindowChange(request)
				windowsize <- WindowSize{Width: width, Height: height}
				// No reply to window change
				//request.Reply(true, nil)
			default:
				request.Reply(false, nil)
			}
		}
	}()

	reader := bufio.NewReader(channel)
	return handler.Handle(reader, t, windowsize)
}
Exemple #29
0
func proxyChannel(c *ConnInsight, mcha ssh.Channel, mreq <-chan *ssh.Request, master ssh.NewChannel, client *SSHClient, killer <-chan struct{}) error {

	do := new(sync.Once)
	cochan, coreq, err := client.OpenChannel(master.ChannelType(), master.ExtraData())

	checkError(err, fmt.Sprintf("Creating Client Channel for %s", client.RemoteAddr().String()))

	if err != nil {
		return err
	}

	stop := make(chan struct{})
	endClose := func() { close(stop) }

	flux.GoDefer("proxyChannelCopy", func() {
		defer cochan.Close()
		defer mcha.Close()

		func() {
		ploop:
			for {
				select {
				case <-stop:
					break ploop
				case <-killer:
					break ploop
				case slx, ok := <-coreq:
					if !ok {
						return
					}

					Reply(slx, mcha, c)

					switch slx.Type {
					case "exit-status":
						break ploop
					}

				case mlx, ok := <-mreq:
					if !ok {
						return
					}

					Reply(mlx, cochan, c)

					switch mlx.Type {
					case "exit-status":
						break ploop
					}
				}
			}
		}()
	})

	mastercloser := io.ReadCloser(mcha)
	slavecloser := io.ReadCloser(cochan)

	wrapmaster := io.MultiWriter(mcha, c.Out())
	wrapsl := io.MultiWriter(cochan, c.In())

	flux.GoDefer("CopyToSlave", func() {
		defer do.Do(endClose)
		io.Copy(wrapsl, mastercloser)
	})

	flux.GoDefer("CopyToMaster", func() {
		defer do.Do(endClose)
		io.Copy(wrapmaster, slavecloser)
	})

	flux.GoDefer("CopyCloser", func() {
		defer c.Close()

		<-stop

		mx := mastercloser.Close()
		checkError(mx, "Master Writer Closer")

		sx := slavecloser.Close()
		checkError(sx, "Slave Writer Closer")

		ex := client.Close()
		checkError(ex, "Client Writer Closer")
	})

	return nil
}
Exemple #30
-1
func pipe(ch ssh.Channel, client *ssh.Client, session *ssh.Session, command string) (int, error) {
	targetStderr, err := session.StderrPipe()
	if err != nil {
		return -1, errors.New("fail to pipe stderr: " + err.Error())
	}
	targetStdout, err := session.StdoutPipe()
	if err != nil {
		return -1, errors.New("fail to pipe stdout: " + err.Error())
	}
	targetStdin, err := session.StdinPipe()
	if err != nil {
		return -1, errors.New("fail to pipe stdin: " + err.Error())
	}

	go io.Copy(targetStdin, ch)
	go io.Copy(ch.Stderr(), targetStderr)
	go io.Copy(ch, targetStdout)

	err = session.Start(command)
	if err != nil {
		ch.Write([]byte("Error when starting '" + command + "': " + err.Error()))
		ch.Close()
	}

	err = session.Wait()
	if err != nil {
		if err, ok := err.(*ssh.ExitError); ok {
			return err.ExitStatus(), nil
		} else {
			return -1, errors.New("failed to wait ssh command: " + err.Error())
		}
	}

	return 0, nil
}