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) } } }
// 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") }
func (handle *handle) Request(ch ssh.Channel, req *ssh.Request) (bool, error) { config.Log.Debug("handle %s", req.Type) switch req.Type { case "pty-req": fallthrough case "shell": return true, ShellDisabled case "env": // we store these off?? return false, nil case "exec": // it is prefixed with the length so we strip it off command := string(req.Payload[4:]) config.Log.Debug("handle cmd %s", command) // find the correct handler and run it config.Log.Debug("looking for command in %d", len(repo.Commands())) for _, cmd := range repo.Commands() { if cmd.Match(command) { config.Log.Debug("found match", command) code, err := cmd.Run(command, ch) exitStatusBuffer := make([]byte, 4) binary.PutUvarint(exitStatusBuffer, uint64(code)) config.Log.Debug("cmd finished", code, err) // purposefully ignoring the possible error ch.SendRequest("exit-status", false, exitStatusBuffer) return true, err } } return true, UnknownCommand } return true, UnkownRequest }
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 }
func sendCmdResult(channel ssh.Channel, result []byte, statusCode uint32) error { if _, err := channel.Write(result); err != nil { return fmt.Errorf("failed to write to ssh-channel: %v", err) } status := struct { Status uint32 }{ statusCode, } _, err := channel.SendRequest("exit-status", false, ssh.Marshal(&status)) if err != nil { return fmt.Errorf("failed to SendRequest: %v", err) } return nil }
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 } } }
//Reply handles the operation between a req and channel func Reply(req *ssh.Request, dest ssh.Channel, c *ConnInsight) { dx, err := dest.SendRequest(req.Type, req.WantReply, req.Payload) checkError(err, fmt.Sprintf("Request %s processed", req.Type)) if req.WantReply { req.Reply(dx, nil) } meta := map[string]interface{}{ "type": "reply", "name": req.Type, "payload": req.Payload, } c.Aux().Emit(meta) }
func forwardRequest(req *ssh.Request, channel ssh.Channel) error { if string(req.Type) != "subsystem" && string(req.Type) != "exit-status" { req.Reply(false, nil) if req.Type == "env" { return nil } return fmt.Errorf("Ignoring unsupported request type: %s", string(req.Type)) } reply, err := channel.SendRequest(req.Type, req.WantReply, req.Payload) if err != nil { return err } if req.WantReply { req.Reply(reply, nil) } return nil }
/* 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, ) } }
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()) Expect(accepted).To(BeFalse()) }) }) Context("and a signal request fails to unmarshal", func() { It("rejects the request", func() {
func sendExitStatus(status uint32, channel ssh.Channel) error { exit := struct{ Status uint32 }{uint32(0)} _, err := channel.SendRequest("exit-status", false, ssh.Marshal(exit)) return err }
func (p *SshConn) serve() error { serverConn, chans, reqs, err := ssh.NewServerConn(p, p.config) if err != nil { log.Println("failed to handshake") return (err) } defer serverConn.Close() clientConn, err := p.callbackFn(serverConn) if err != nil { log.Printf("%s", err.Error()) return (err) } defer clientConn.Close() go ssh.DiscardRequests(reqs) for newChannel := range chans { channel2, requests2, err2 := clientConn.OpenChannel(newChannel.ChannelType(), newChannel.ExtraData()) if err2 != nil { log.Printf("Could not accept client channel: %s", err.Error()) return err } channel, requests, err := newChannel.Accept() if err != nil { log.Printf("Could not accept server channel: %s", err.Error()) return err } // connect requests go func() { log.Printf("Waiting for request") r: for { var req *ssh.Request var dst ssh.Channel select { case req = <-requests: dst = channel2 case req = <-requests2: dst = channel } log.Printf("Request: %s %s %s %s\n", dst, req.Type, req.WantReply, req.Payload) b, err := dst.SendRequest(req.Type, req.WantReply, req.Payload) if err != nil { log.Printf("%s", err) } if req.WantReply { req.Reply(b, nil) } switch req.Type { case "exit-status": break r case "exec": // not supported (yet) default: log.Println(req.Type) } } channel.Close() channel2.Close() }() // connect channels log.Printf("Connecting channels.") var wrappedChannel io.ReadCloser = channel var wrappedChannel2 io.ReadCloser = channel2 if p.wrapFn != nil { // wrappedChannel, err = p.wrapFn(channel) wrappedChannel2, err = p.wrapFn(serverConn, channel2) } go io.Copy(channel2, wrappedChannel) go io.Copy(channel, wrappedChannel2) defer wrappedChannel.Close() defer wrappedChannel2.Close() } if p.closeFn != nil { p.closeFn(serverConn) } return nil }
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))) }
func (self *SSHConn) serve() error { serverConn, chans, reqs, err := ssh.NewServerConn(self.Conn, self.config) if err != nil { return err } defer serverConn.Close() clientConn, err := getClient(serverConn) if err != nil { return err } defer clientConn.Close() go ssh.DiscardRequests(reqs) for newChannel := range chans { remoteChannel, remoteRequest, err := clientConn.OpenChannel(newChannel.ChannelType(), newChannel.ExtraData()) if err != nil { return err } localChannel, localRequest, err := newChannel.Accept() if err != nil { return err } // connect requests go func() { logs.Debug("Waiting for request") r: for { var req *ssh.Request var dst ssh.Channel select { case req = <-localRequest: dst = remoteChannel logs.Debug("from local to remote") case req = <-remoteRequest: dst = localChannel logs.Debug("from remote to local") } if req == nil { break } logs.Debug("Request", req.Type, req.WantReply) b, err := dst.SendRequest(req.Type, req.WantReply, req.Payload) if err != nil { logs.Info(err) } if req.WantReply { req.Reply(b, nil) } switch req.Type { case "exit-status": break r } } }() // connect channels logs.Debug("Connecting channels") go func() { defer remoteChannel.Close() io.Copy(remoteChannel, localChannel) remoteChannel.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) }() go func() { defer localChannel.Close() io.Copy(localChannel, remoteChannel) localChannel.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) }() } closeConn(serverConn) return nil }
func (server *Server) handleExecRequest(channel ssh.Channel, request *ssh.Request, conn *ssh.ServerConn) { doReply := func(ok bool) { err := request.Reply(ok, nil) if err != nil { server.Logger.Errorf("Failed to reply %t to SSH Request from %s due to %s", ok, conn.RemoteAddr().String(), err) } server.Logger.Debugf("Reply to SSH Request `%t` from %s", ok, conn.RemoteAddr().String()) } if len(request.Payload) < 4 { server.Logger.Errorf("Payload must not be shorter than 4 bytes, but only %d bytes", len(request.Payload)) doReply(false) return } header := request.Payload[:4] cmdLen := int64(binary.BigEndian.Uint32(header)) if int64(len(request.Payload)) < 4+cmdLen { server.Logger.Errorf("Payload must not be shorter than %d bytes, but only %d bytes", 4+cmdLen, len(request.Payload)) doReply(false) return } cmd := request.Payload[4 : 4+cmdLen] server.Logger.Debugf("Execute command `%s` via SSH from %s", string(cmd), conn.RemoteAddr().String()) shellCmd := exec.Command(server.Config.ShellPath, "-c", string(cmd)) stdinPipe, err := shellCmd.StdinPipe() if err != nil { server.Logger.Errorf("Failed to create STDIN pipe error for command: %s", err) doReply(false) return } defer stdinPipe.Close() stdoutPipe, err := shellCmd.StdoutPipe() if err != nil { server.Logger.Errorf("Failed to create STDOUT pipe error for command: %s", err) doReply(false) return } defer stdoutPipe.Close() stderrPipe, err := shellCmd.StderrPipe() if err != nil { server.Logger.Errorf("Failed to create STDERR pipe error for command: %s", err) doReply(false) return } defer stderrPipe.Close() sendExitStatus := func() { channel.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) server.Logger.Debugf("Sent exit status 0 to %s", conn.RemoteAddr().String()) } var once sync.Once go func() { io.Copy(stdinPipe, channel) once.Do(sendExitStatus) }() go func() { io.Copy(channel, stdoutPipe) once.Do(sendExitStatus) }() go func() { io.Copy(channel.Stderr(), stderrPipe) once.Do(sendExitStatus) }() err = shellCmd.Start() if err != nil { server.Logger.Errorf("Close SSH Channel from %s due to command error: %s", conn.RemoteAddr().String(), err) doReply(false) return } doReply(true) _, err = shellCmd.Process.Wait() if err != nil { _, ok := err.(*exec.ExitError) if !ok { server.Logger.Errorf("Failed to wait command(PID = %d) due to %s", shellCmd.Process.Pid, err) } return } }