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 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) } }
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 handleChannel(s *Stream, ch ssh.Channel, reqs <-chan *ssh.Request, handle func(*Stream)) { // handle requests receive for this Channel go func(in <-chan *ssh.Request) { for req := range in { logrus.Debugln("AdminTool -> Request of type:", req.Type, "len:", len(req.Type)) logrus.Debugln("AdminTool -> Request payload:", string(req.Payload), "len:", len(req.Payload)) if req.WantReply { req.Reply(false, nil) } } logrus.Debugln("AdminTool -> End of request GO chan") }(reqs) // read data from channel go func() { for { buffer := make([]byte, 64) n, err := ch.Read(buffer) if err != nil { if err.Error() == "EOF" { handleData(s, []byte{}, true) // all data received: handle Stream message handle(s) break } else { logrus.Fatalln("failed to read channel : " + err.Error()) } } handleData(s, buffer[:n], false) } }() }
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) } } }
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 } } } }
func attachCmd(cmd *exec.Cmd, ch ssh.Channel) (*sync.WaitGroup, error) { var wg sync.WaitGroup wg.Add(3) stdout, err := cmd.StdoutPipe() if err != nil { return nil, err } stderr, err := cmd.StderrPipe() if err != nil { return nil, err } stdin, err := cmd.StdinPipe() if err != nil { return nil, err } go func() { defer wg.Done() io.Copy(stdin, ch) }() go func() { defer wg.Done() io.Copy(ch.Stderr(), stderr) }() go func() { defer wg.Done() io.Copy(ch, stdout) }() return &wg, nil }
func pipe(dst, src ssh.Channel) { _, err := io.Copy(dst, src) if err != nil { fmt.Println(err.Error()) } dst.CloseWrite() }
// Ping handles a simple test SSH exec. // // Returns the string PONG and exit status 0. // // Params: // - channel (ssh.Channel): The channel to respond on. // - request (*ssh.Request): The request. // func Ping(channel ssh.Channel, req *ssh.Request) error { log.Info("PING") if _, err := channel.Write([]byte("pong")); err != nil { log.Err("Failed to write to channel: %s", err) } sendExitStatus(0, channel) req.Reply(true, nil) return nil }
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 }
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() }
// plumbCommand connects the exec in/output and the channel in/output. // // The sidechannel is for sending errors to logs. func plumbCommand(cmd *exec.Cmd, channel ssh.Channel, sidechannel io.Writer) *sync.WaitGroup { var wg sync.WaitGroup inpipe, _ := cmd.StdinPipe() go func() { io.Copy(inpipe, channel) inpipe.Close() }() cmd.Stdout = channel cmd.Stderr = channel.Stderr() return &wg }
func (push Push) Run(command string, ch ssh.Channel) (uint64, error) { //TODO make "master" be dynamic code, err := gitShell(ch, ch.Stderr(), command) if err == nil { newCommit := getCommit("master") stream := ch.Stderr() err = deploy.Run(stream, newCommit) if err != nil { return 1, err } } return code, err }
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) } } } }
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 (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) }
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 }
//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 (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 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) } }
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 }
func handleExec(ch ssh.Channel, req *ssh.Request) { cmd := string(req.Payload[4:]) log.Println("received cmd", cmd) var msg string switch cmd { case "open", "close": err := sendCommand(cmd) if err != nil { log.Println("could not write to ctrl sock:", err) msg = "error: could not write to socket" } else { log.Printf("sent command '%s' to ctrl", cmd) msg = "ok" } default: msg = "invalid command " + cmd } ch.Write([]byte(msg + "\r\n")) }
/* 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, ) } }
func (s *scpRequest) ParseSCPRequest(channel ssh.Channel, req *ssh.Request) error { var err error // Parse the payload received from the scp client if s.original, err = parsePayload(req.Payload); err != nil { return err } log.Println("being sent file:", s.original) // Acknowledge payload. if _, err = channel.Write(zeroByte); err != nil { return errors.Wrap(err, "failed to write") } // Receive SCP Header scpHeader := make([]byte, 2048) // size of buf in openssh if _, err = channel.Read(scpHeader); err != nil { return errors.Wrap(err, "failed to retrieve header") } if _, s.size, _, err = parseHeader(scpHeader); err != nil { return errors.Wrap(err, "failed to parse scp header") } // Acknowledge We have received the SCP Header if _, err = channel.Write(zeroByte); err != nil { return errors.Wrap(err, "failed to reply to scp header") } return nil }
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 }
// 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) 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 }
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() }
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 } } }
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 }