// HandleChannel handles one SSH channel func (c *Client) HandleChannel(newChannel ssh.NewChannel) error { if newChannel.ChannelType() != "session" { log.Debugf("Unknown channel type: %s", newChannel.ChannelType()) newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") return nil } channel, requests, err := newChannel.Accept() if err != nil { log.Errorf("newChannel.Accept failed: %v", err) return err } c.ChannelIdx++ log.Debugf("HandleChannel.channel (client=%d channel=%d)", c.Idx, c.ChannelIdx) log.Debug("Creating pty...") c.Pty, c.Tty, err = pty.Open() if err != nil { log.Errorf("pty.Open failed: %v", err) return nil } c.HandleChannelRequests(channel, requests) return nil }
// Action is the default cli action to execute func Action(c *cli.Context) { // Initialize the SSH server server, err := ssh2docker.NewServer() if err != nil { log.Fatalf("Cannot create server: %v", err) } // Restrict list of allowed images if c.String("allowed-images") != "" { server.AllowedImages = strings.Split(c.String("allowed-images"), ",") } // Configure server server.DefaultShell = c.String("shell") server.DockerRunArgsInline = c.String("docker-run-args") server.DockerExecArgsInline = c.String("docker-exec-args") server.NoJoin = c.Bool("no-join") server.CleanOnStartup = c.Bool("clean-on-startup") server.PasswordAuthScript = c.String("password-auth-script") server.PublicKeyAuthScript = c.String("publickey-auth-script") server.LocalUser = c.String("local-user") server.Banner = c.String("banner") // Register the SSH host key hostKey := c.String("host-key") switch hostKey { case "built-in": hostKey = DefaultHostKey case "system": hostKey = "/etc/ssh/ssh_host_rsa_key" } err = server.AddHostKey(hostKey) if err != nil { log.Fatalf("Cannot add host key: %v", err) } // Bind TCP socket bindAddress := c.String("bind") listener, err := net.Listen("tcp", bindAddress) if err != nil { log.Fatalf("Failed to start listener on %q: %v", bindAddress, err) } log.Infof("Listening on %q", bindAddress) // Initialize server if err = server.Init(); err != nil { log.Fatalf("Failed to initialize the server: %v", err) } // Accept new clients for { conn, err := listener.Accept() if err != nil { log.Errorf("Accept failed: %v", err) continue } go server.Handle(conn) } }
func httpStatusFromCode(code codes.Code) int { switch code { case codes.OK: return http.StatusOK case codes.Canceled: return http.StatusRequestTimeout case codes.Unknown: return http.StatusInternalServerError case codes.InvalidArgument: return http.StatusBadRequest case codes.DeadlineExceeded: return http.StatusRequestTimeout case codes.NotFound: return http.StatusNotFound case codes.AlreadyExists: return http.StatusConflict case codes.PermissionDenied: return http.StatusForbidden case codes.Unauthenticated: return http.StatusUnauthorized case codes.ResourceExhausted: return http.StatusForbidden case codes.FailedPrecondition: return http.StatusPreconditionFailed case codes.Aborted: return http.StatusConflict case codes.OutOfRange: return http.StatusBadRequest case codes.Unimplemented: return http.StatusNotImplemented case codes.Internal: return http.StatusInternalServerError case codes.Unavailable: return http.StatusServiceUnavailable case codes.DataLoss: return http.StatusInternalServerError } log.Errorf("Unknown GRPC error code: %v", code) return http.StatusInternalServerError }
// HandleChannelRequests handles channel requests func (c *Client) HandleChannelRequests(channel ssh.Channel, requests <-chan *ssh.Request) { go func(in <-chan *ssh.Request) { defer c.Tty.Close() for req := range in { ok := false switch req.Type { case "shell": log.Debugf("HandleChannelRequests.req shell") if len(req.Payload) != 0 { break } ok = true entrypoint := "" if c.Config.EntryPoint != "" { entrypoint = c.Config.EntryPoint } var args []string if c.Config.Command != nil { args = c.Config.Command } if entrypoint == "" && len(args) == 0 { args = []string{c.Server.DefaultShell} } c.runCommand(channel, entrypoint, args) case "exec": command := string(req.Payload[4:]) log.Debugf("HandleChannelRequests.req exec: %q", command) ok = true args, err := shlex.Split(command) if err != nil { log.Errorf("Failed to parse command %q: %v", command, args) } c.runCommand(channel, c.Config.EntryPoint, args) case "pty-req": ok = true c.Config.UseTTY = true termLen := req.Payload[3] c.Config.Env["TERM"] = string(req.Payload[4 : termLen+4]) c.Config.Env["USE_TTY"] = "1" w, h := ttyhelper.ParseDims(req.Payload[termLen+4:]) ttyhelper.SetWinsize(c.Pty.Fd(), w, h) log.Debugf("HandleChannelRequests.req pty-req: TERM=%q w=%q h=%q", c.Config.Env["TERM"], int(w), int(h)) case "window-change": w, h := ttyhelper.ParseDims(req.Payload) ttyhelper.SetWinsize(c.Pty.Fd(), w, h) continue case "env": keyLen := req.Payload[3] key := string(req.Payload[4 : keyLen+4]) valueLen := req.Payload[keyLen+7] value := string(req.Payload[keyLen+8 : keyLen+8+valueLen]) log.Debugf("HandleChannelRequets.req 'env': %s=%q", key, value) c.Config.Env[key] = value default: log.Debugf("Unhandled request type: %q: %v", req.Type, req) } if req.WantReply { if !ok { log.Debugf("Declining %s request...", req.Type) } req.Reply(ok, nil) } } }(requests) }
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") }
// Errorf level formatted message. func Errorf(msg string, v ...interface{}) { apexlog.Errorf(msg, v...) }