package ssh import ( "docker4ssh/docker" "fmt" "go.uber.org/zap" "golang.org/x/crypto/ssh" ) type RequestType string const ( RequestPtyReq RequestType = "pty-req" RequestWindowChange RequestType = "window-change" ) type PtyReqPayload struct { Term string Width, Height uint32 PixelWidth, PixelHeight uint32 Modes []byte } func handleChannels(chans <-chan ssh.NewChannel, client *docker.Client, user *User) { for channel := range chans { go handleChannel(channel, client, user) } } func handleChannel(channel ssh.NewChannel, client *docker.Client, user *User) { if t := channel.ChannelType(); t != "session" { channel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t)) return } conn, requests, err := channel.Accept() if err != nil { zap.S().Warnf("Failed to accept channel for user %s", user.ID) return } defer conn.Close() user.Terminal.ReadWriter = conn // handle all other request besides the normal user input. // currently, only 'pty-req' is implemented which determines a terminal size change go handleRequest(requests, user) // this handles the actual user terminal connection. // blocks until the session has finished connection(client, user) zap.S().Debugf("Session for user %s ended", user.ID) } func handleRequest(requests <-chan *ssh.Request, user *User) { for request := range requests { switch RequestType(request.Type) { case RequestPtyReq: // this could spam the logs when the user resizes his window constantly // log() var ptyReq PtyReqPayload ssh.Unmarshal(request.Payload, &ptyReq) user.Terminal.Width = ptyReq.Width user.Terminal.Height = ptyReq.Height case RequestWindowChange: // prevent from logging default: zap.S().Debugf("New request from user %s - Type: %s, Want Reply: %t, Payload: '%s'", user.ID, request.Type, request.WantReply, request.Payload) } if request.WantReply { request.Reply(true, nil) } } }