func proxy(w io.Writer, r io.Reader, e io.Reader, interactiveMap map[string]string) (chan<- string, <-chan string, chan string) {
	inputChannel := make(chan string, 1)
	outputChannel := make(chan string, 1)
	errorChannel := make(chan string, 1024)

	// Issue command
	go func() {
		for cmd := range inputChannel {
			w.Write([]byte(cmd))
		}
	}()

	// Handle responsed error
	go func() {
		for {
			text, _, err := ioutility.ReadText(e, 1024*64)
			if err == io.EOF {
				close(errorChannel)
				return
			} else if err != nil {
				errorChannel <- err.Error()
				close(errorChannel)
				return
			}

			// Upon receiveing from stderr, send to error
			errorChannel <- text
		}
	}()

	// Handle responsed output
	go func() {
		for {
			text, _, err := ioutility.ReadText(r, 1024*16)
			if err == io.EOF {
				outputChannel <- text
				close(outputChannel)
				return
			} else if err != nil {
				outputChannel <- err.Error()
				close(outputChannel)
				return
			}

			for key, value := range interactiveMap {
				if strings.Contains(text, key) {
					w.Write([]byte(value))
					break
				}
			}

			outputChannel <- text
		}
	}()
	return inputChannel, outputChannel, errorChannel
}
예제 #2
0
func ProxyServer(ws *websocket.Conn) {
	cloudoneProtocol := beego.AppConfig.String("cloudoneProtocol")
	cloudoneHost := beego.AppConfig.String("cloudoneHost")
	cloudonePort := beego.AppConfig.String("cloudonePort")

	parameterMap := ws.Request().URL.Query()
	widthSlice := parameterMap["width"]
	heightSlice := parameterMap["height"]
	hostIPSlice := parameterMap["hostIP"]
	containerIDSlice := parameterMap["containerID"]
	tokenSlice := parameterMap["token"]

	if len(widthSlice) != 1 {
		errorMessage := "Parameter width is incorrect"
		ws.Write([]byte(errorMessage))
		ws.Close()
		return
	}
	width, err := strconv.Atoi(widthSlice[0])
	if err != nil {
		errorMessage := "Format of parameter width is incorrect"
		ws.Write([]byte(errorMessage))
		ws.Close()
		return
	}

	if len(heightSlice) != 1 {
		errorMessage := "Parameter height is incorrect"
		ws.Write([]byte(errorMessage))
		ws.Close()
		return
	}
	height, err := strconv.Atoi(heightSlice[0])
	if err != nil {
		errorMessage := "Format of parameter height is incorrect"
		ws.Write([]byte(errorMessage))
		ws.Close()
		return
	}

	if len(hostIPSlice) != 1 {
		errorMessage := "Parameter hostIP is incorrect"
		ws.Write([]byte(errorMessage))
		ws.Close()
		return
	}
	hostIP := hostIPSlice[0]

	if len(containerIDSlice) != 1 {
		errorMessage := "Parameter containerID is incorrect"
		ws.Write([]byte(errorMessage))
		ws.Close()
		return
	}
	containerID := containerIDSlice[0]
	// Remove docker protocol prefix docker://
	containerID = containerID[9:]

	if len(tokenSlice) != 1 {
		errorMessage := "Parameter token is incorrect"
		ws.Write([]byte(errorMessage))
		ws.Close()
		return
	}
	token := tokenSlice[0]
	headerMap := make(map[string]string)
	headerMap["token"] = token

	url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/hosts/credentials/" + hostIP

	credential := Credential{}

	_, err = restclient.RequestGetWithStructure(url, &credential, headerMap)

	if identity.IsTokenInvalid(err) {
		ws.Write([]byte(err.Error()))
		ws.Close()
		return
	}

	if err != nil {
		ws.Write([]byte(err.Error()))
		ws.Close()
		return
	}

	interactiveMap := make(map[string]string)
	interactiveMap["[sudo]"] = credential.SSH.Password + "\n"
	interactiveMap["exit"] = "exit\n"

	// Command way
	sshCommandProxy := sshclient.CreateSSHCommandProxy(
		2*time.Second,
		credential.IP,
		credential.SSH.Port,
		credential.SSH.User,
		credential.SSH.Password,
		height,
		width,
		interactiveMap)
	i, o, _, err := sshCommandProxy.Connect()

	if err != nil {
		ws.Write([]byte(err.Error()))
		ws.Close()
		return
	}

	i <- "sudo docker exec -it " + containerID + " bash\n"

	go func() {
		for {
			data, ok := <-o
			if ok == false {
				break
			} else {
				ws.Write([]byte(data))
			}
		}
		ws.Close()
	}()

	for {
		data, _, err := ioutility.ReadText(ws, 256)
		if err == io.EOF {
			break
		} else if err != nil {
			ws.Write([]byte(err.Error()))
			break
		}

		i <- data
	}
	ws.Close()
	sshCommandProxy.Disconnect()

	// Stream way
	/*
		sshStreamProxy := sshclient.CreateSSHStreamProxy(
			2*time.Second,
			credential.IP,
			credential.SSH.Port,
			credential.SSH.User,
			credential.SSH.Password,
			height,
			width)
		w, r, _, err := sshStreamProxy.Connect()

		if err != nil {
			ws.Write([]byte(err.Error()))
			ws.Close()
			return
		}

		w.Write([]byte("sudo docker exec -it " + containerID + " bash\n"))
		w.Write([]byte(credential.SSH.Password + "\n"))

		go func() {
			// Cancatenate ssh reader to websocket writer
			io.Copy(ws, r)

			ws.Close()
		}()

		// Cancatenate websocket reader to ssh writer
		io.Copy(w, ws)

		ws.Close()
		sshStreamProxy.Disconnect()
	*/
}
예제 #3
0
func shell(w io.Writer, r io.Reader, e io.Reader, interactiveMap map[string]string) (chan<- string, <-chan string, chan string) {
	inputChannel := make(chan string, 1)
	outputChannel := make(chan string, 1)
	errorChannel := make(chan string, 1024)

	waitGroup := sync.WaitGroup{}
	// Start from read
	waitGroup.Add(1)

	// Issue command
	go func() {
		for cmd := range inputChannel {
			waitGroup.Add(1)
			w.Write([]byte(cmd))
			waitGroup.Wait()
		}
	}()

	// Handle responsed error
	go func() {
		for {
			text, _, err := ioutility.ReadText(e, 1024*64)
			if err == io.EOF {
				close(errorChannel)
				return
			} else if err != nil {
				errorChannel <- err.Error()
				close(errorChannel)
				return
			}

			// Upon receiveing from stderr, send to error
			errorChannel <- text
		}
	}()

	// Handle responsed output
	go func() {
		buffer := bytes.Buffer{}
		length := 0
		for {
			text, n, err := ioutility.ReadText(r, 1024*16)
			if err == io.EOF {
				buffer.WriteString(text)
				outputChannel <- buffer.String()
				close(outputChannel)
				return
			} else if err != nil {
				outputChannel <- err.Error()
				close(outputChannel)
				return
			}

			interactive := false
			for key, value := range interactiveMap {
				if strings.Contains(text, key) {
					w.Write([]byte(value))
					interactive = true
					break
				}
			}

			if interactive {
				// Ignore the response for output
			} else {
				length += n
				_, err := buffer.WriteString(text)
				if err != nil {
					outputChannel <- err.Error()
					close(outputChannel)
					return
				}
			}

			// Keep buffing until the end of this interactive command.
			// $ is the terminal symbol where is used to tell user to enter next command.
			buf := buffer.Bytes()

			if length-2 > 0 && buf[length-2] == '$' {
				text := string(buf[:length])
				outputChannel <- text
				length = 0
				buffer.Reset()
				waitGroup.Done()
			}
		}
	}()
	return inputChannel, outputChannel, errorChannel
}