Example #1
0
// 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
}
Example #2
0
// 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)
	}
}
Example #3
0
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
}
Example #4
0
// 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)
}
Example #5
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")
}
Example #6
0
// Errorf level formatted message.
func Errorf(msg string, v ...interface{}) {
	apexlog.Errorf(msg, v...)
}