Exemple #1
0
// HandleChannel handles one SSH channel
func (c *Client) HandleChannel(newChannel ssh.NewChannel) error {
	if newChannel.ChannelType() != "session" {
		logrus.Debugf("Unknown channel type: %s", newChannel.ChannelType())
		newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
		return nil
	}

	channel, requests, err := newChannel.Accept()
	if err != nil {
		logrus.Errorf("newChannel.Accept failed: %v", err)
		return err
	}
	c.ChannelIdx++
	logrus.Debugf("HandleChannel.channel (client=%d channel=%d): %v", c.Idx, c.ChannelIdx, channel)

	logrus.Debug("Creating pty...")
	f, tty, err := pty.Open()
	if err != nil {
		logrus.Errorf("pty.Open failed: %v", err)
		return nil
	}
	c.Tty = tty
	c.Pty = f

	c.HandleChannelRequests(channel, requests)

	return nil
}
Exemple #2
0
// KeyboardInteractiveCallback is called after PublicKeyCallback
func (s *Server) KeyboardInteractiveCallback(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
	username := conn.User()
	clientID := conn.RemoteAddr().String()
	logrus.Debugf("KeyboardInteractiveCallback: %q %q", username, challenge)

	config := s.ClientConfigs[clientID]
	if config == nil {
		s.ClientConfigs[clientID] = &ClientConfig{
			RemoteUser: username,
			ImageName:  username,
			Keys:       []string{},
			Env:        make(Environment, 0),
		}
	}
	config = s.ClientConfigs[clientID]

	if len(config.Keys) == 0 {
		logrus.Warnf("No user keys, continuing with password authentication")
		return nil, s.CheckConfig(config)
	}

	if s.PublicKeyAuthScript != "" {
		logrus.Debugf("%d keys received, trying to authenticate using hook script", len(config.Keys))
		script, err := expandUser(s.PublicKeyAuthScript)
		if err != nil {
			logrus.Warnf("Failed to expandUser: %v", err)
			return nil, err
		}
		args := append([]string{username}, config.Keys...)
		cmd := exec.Command(script, args...)
		// FIXME: redirect stderr to logrus
		cmd.Stderr = os.Stderr
		output, err := cmd.Output()
		if err != nil {
			logrus.Warnf("Failed to execute publickey-auth-script: %v", err)
			return nil, err
		}

		err = json.Unmarshal(output, &config)
		if err != nil {
			logrus.Warnf("Failed to unmarshal json %q: %v", string(output), err)
			return nil, err
		}
	} else {
		logrus.Debugf("%d keys received, but no hook script, continuing", len(config.Keys))
	}

	return nil, s.CheckConfig(config)
}
Exemple #3
0
// Handle is the SSH client entrypoint, it takes a net.Conn
// instance and handle all the ssh and ssh2docker stuff
func (s *Server) Handle(netConn net.Conn) error {
	if err := s.Init(); err != nil {
		return err
	}

	logrus.Debugf("Server.Handle netConn=%v", netConn)
	// Initialize a Client object
	conn, chans, reqs, err := ssh.NewServerConn(netConn, s.SshConfig)
	if err != nil {
		return err
	}
	client := NewClient(conn, chans, reqs, s)
	client.Config = s.ClientConfigs[conn.RemoteAddr().String()]
	client.Config.Env.ApplyDefaults()

	// Handle requests
	if err = client.HandleRequests(); err != nil {
		return err
	}

	// Handle channels
	if err = client.HandleChannels(); err != nil {
		return err
	}
	return nil
}
Exemple #4
0
// DockerRemove removes a container
func DockerRemove(containerID string) error {
	cmd := exec.Command("docker", "rm", "-f", containerID)
	_, err := cmd.CombinedOutput()
	if err != nil {
		return err
	}
	logrus.Debugf("Deleted container: %q", containerID)
	return nil
}
Exemple #5
0
// DockerKill kills a container
func DockerKill(containerID string) error {
	cmd := exec.Command("docker", "kill", "-s", "9", containerID)
	_, err := cmd.CombinedOutput()
	if err != nil {
		return err
	}
	logrus.Debugf("Killed container: %q", containerID)
	return nil
}
Exemple #6
0
// HandleRequests handles SSH requests
func (c *Client) HandleRequests() error {
	go func(in <-chan *ssh.Request) {
		for req := range in {
			logrus.Debugf("HandleRequest: %v", req)
			if req.WantReply {
				req.Reply(false, nil)
			}
		}
	}(c.Reqs)
	return nil
}
Exemple #7
0
// PasswordCallback is called when the user tries to authenticate using a password
func (s *Server) PasswordCallback(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
	username := conn.User()
	clientID := conn.RemoteAddr().String()

	logrus.Debugf("PasswordCallback: %q %q", username, password)

	config := s.ClientConfigs[clientID]
	if config == nil {
		s.ClientConfigs[clientID] = &ClientConfig{
			//Allowed: true,
			RemoteUser: username,
			ImageName:  username,
			Keys:       []string{},
			Env:        make(Environment, 0),
		}
	}
	config = s.ClientConfigs[clientID]

	if s.PasswordAuthScript != "" {
		script, err := expandUser(s.PasswordAuthScript)
		if err != nil {
			logrus.Warnf("Failed to expandUser: %v", err)
			return nil, err
		}
		cmd := exec.Command(script, username, string(password))
		// FIXME: redirect stderr to logrus
		cmd.Stderr = os.Stderr
		output, err := cmd.Output()
		if err != nil {
			logrus.Warnf("Failed to execute password-auth-script: %v", err)
			return nil, err
		}

		err = json.Unmarshal(output, &config)
		if err != nil {
			logrus.Warnf("Failed to unmarshal json %q: %v", string(output), err)
			return nil, err
		}
	}

	return nil, s.CheckConfig(config)
}
Exemple #8
0
// PublicKeyCallback is called when the user tries to authenticate using an SSH public key
func (s *Server) PublicKeyCallback(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
	username := conn.User()
	clientID := conn.RemoteAddr().String()
	keyText := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key)))
	logrus.Debugf("PublicKeyCallback: %q %q", username, keyText)
	// sessionID := conn.SessionID()

	config := s.ClientConfigs[clientID]
	if config == nil {
		s.ClientConfigs[clientID] = &ClientConfig{
			RemoteUser: username,
			ImageName:  username,
			Keys:       []string{},
			Env:        make(Environment, 0),
		}
	}
	config = s.ClientConfigs[clientID]
	config.Keys = append(config.Keys, keyText)
	return nil, s.CheckConfig(config)
}
Exemple #9
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":
				logrus.Debugf("HandleChannelRequests.req shell")
				if len(req.Payload) != 0 {
					break
				}
				ok = true

				var cmd *exec.Cmd
				var err error

				if c.Config.IsLocal {
					cmd = exec.Command("/bin/bash")
				} 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 {
							logrus.Warnf("docker ps ... failed: %v", err)
							continue
						}
						existingContainer = strings.TrimSpace(string(buf))
					}

					// Opening Docker process
					if existingContainer != "" {
						// Attaching to an existing container
						shell := c.Server.DefaultShell
						if c.Config.EntryPoint != "" {
							shell = c.Config.EntryPoint
						}
						args := []string{"exec", "-it", existingContainer, shell}
						logrus.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"}
						args = append(args, c.Server.DockerRunArgs...)
						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 c.Config.EntryPoint != "" {
							args = append(args, "--entrypoint", c.Config.EntryPoint)
						}
						args = append(args, c.Config.ImageName)
						if c.Config.Command != nil {
							args = append(args, c.Config.Command...)
						} else {
							args = append(args, c.Server.DefaultShell)
						}
						logrus.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 = c.Tty
				cmd.Stdin = c.Tty
				cmd.Stderr = c.Tty
				cmd.SysProcAttr = &syscall.SysProcAttr{
					Setctty: true,
					Setsid:  true,
				}

				err = cmd.Start()
				if err != nil {
					logrus.Warnf("cmd.Start failed: %v", err)
					continue
				}

				var once sync.Once
				close := func() {
					channel.Close()
					logrus.Debug("session closed")
				}

				go func() {
					io.Copy(channel, c.Pty)
					once.Do(close)
				}()

				go func() {
					io.Copy(c.Pty, channel)
					once.Do(close)
				}()

				go func() {
					if err := cmd.Wait(); err != nil {
						logrus.Warnf("cmd.Wait failed: %v", err)
					}
					once.Do(close)
				}()

			case "exec":
				command := string(req.Payload)
				logrus.Debugf("HandleChannelRequests.req exec: %q", command)
				ok = false

				fmt.Fprintln(channel, "⚠️  ssh2docker: exec is not yet implemented. https://github.com/moul/ssh2docker/issues/51.")
				time.Sleep(3 * time.Second)

			case "pty-req":
				ok = true
				termLen := req.Payload[3]
				c.Config.Env["TERM"] = string(req.Payload[4 : termLen+4])
				w, h := parseDims(req.Payload[termLen+4:])
				SetWinsize(c.Pty.Fd(), w, h)
				logrus.Debugf("HandleChannelRequests.req pty-req: TERM=%q w=%q h=%q", c.Config.Env["TERM"], int(w), int(h))

			case "window-change":
				w, h := parseDims(req.Payload)
				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])
				logrus.Debugf("HandleChannelRequets.req 'env': %s=%q", key, value)
				c.Config.Env[key] = value

			default:
				logrus.Debugf("Unhandled request type: %q: %v", req.Type, req)
			}

			if req.WantReply {
				if !ok {
					logrus.Debugf("Declining %s request...", req.Type)
				}
				req.Reply(ok, nil)
			}
		}
	}(requests)
}
Exemple #10
0
func SetWinsize(fd uintptr, w, h uint32) {
	logrus.Debugf("Window resize '%dx%d'", w, h)
	ws := &Winsize{Width: uint16(w), Height: uint16(h)}
	syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
}