Docker удаленная оболочка через веб-сокет - PullRequest
0 голосов
/ 25 мая 2020

У меня запущено приложение Django и Docker Setup.
Я пытаюсь добиться, чтобы пользователь приложения Django мог удаленно подключать
к контейнеру через веб-сокет и получать оболочка внутри своего браузера, поэтому ему не нужна дополнительная настройка et c ..
Моя первоначальная мысль примерно такая:

  1. Пользователь отправляет запрос контейнера X на Django.
  2. Django проверяет, аутентифицирован ли пользователь et c.
  3. Django звонки docker
  4. Docker открывает сокет
  5. Django возвращает сокет Frontend (User)

Чтобы проверить этот процесс, я попробовал что-то вроде:

import docker
client = docker.from_env()
container = client.containers.get("Id of conainer X")
result = container.exec_run(cmd="/bin/bash", stdout=True, stdin=True, stderr=True, tty=True, socket=True)

Здесь начинается моя борьба. Отладчики PyCharm сообщают мне
, что result[1] (я предполагаю, это сокет для stdin?) Имеет тип: socket.SocketIO
Как я могу открыть оболочку в моем Frontend с помощью этого сокета?
I Мы рассмотрели Portainer и инструмент под названием docker -box ,
, но они оба используют странный метод Golang для перехвата соединения?
Вот код инструмента docker -box:


func ExecContainer(ws *websocket.Conn) {
    container := ws.Request().URL.Path[len("/exec/"):]
    if container == "" {
        ws.Write([]byte("Container does not exist"))
        return
    }
    type stuff struct {
        Id string
    }
    var s stuff
    params := bytes.NewBufferString("{\"AttachStdin\":true,\"AttachStdout\":true,\"AttachStderr\":true,\"Tty\":true,\"Cmd\":[\"/bin/bash\"]}")
    resp, err := http.Post("http://"+*host+"/containers/"+container+"/exec", "application/json", params)
    if err != nil {
        panic(err)
    }
    data, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
    json.Unmarshal([]byte(data), &s)
    if err := hijack(*host, "POST", "/exec/"+s.Id+"/start", true, ws, ws, ws, nil, nil); err != nil {
        panic(err)
    }
    fmt.Println(ws)
    spew.Dump(ws)
}
func hijack(addr, method, path string, setRawTerminal bool, in io.ReadCloser, stdout, stderr io.Writer, started chan io.Closer, data interface{}) error {

    params := bytes.NewBufferString("{\"Detach\": false, \"Tty\": true}")
    req, err := http.NewRequest(method, path, params)
    if err != nil {
        return err
    }
    req.Header.Set("User-Agent", "Docker-Client")
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Connection", "Upgrade")
    req.Header.Set("Upgrade", "tcp")
    req.Host = addr

    dial, err := net.Dial("tcp", addr)
    if tcpConn, ok := dial.(*net.TCPConn); ok {
        tcpConn.SetKeepAlive(true)
        tcpConn.SetKeepAlivePeriod(30 * time.Second)
    }
    if err != nil {
        return err
    }
    clientconn := httputil.NewClientConn(dial, nil)
    defer os.Exit(0)
    defer clientconn.Close()

    clientconn.Do(req)

    rwc, br := clientconn.Hijack()
    defer rwc.Close()

    if started != nil {
        started <- rwc
    }

    var receiveStdout chan error

    if stdout != nil || stderr != nil {
        go func() (err error) {
            if setRawTerminal && stdout != nil {
                _, err = io.Copy(stdout, br)
            }
            return err
        }()
    }

    go func() error {
        if in != nil {
            io.Copy(rwc, in)
        }

        if conn, ok := rwc.(interface {
            CloseWrite() error
        }); ok {
            if err := conn.CloseWrite(); err != nil {
                os.Exit(0)
            }
        }
        return nil
    }()

    if stdout != nil || stderr != nil {
        if err := <-receiveStdout; err != nil {
            return err
        }
    }
    spew.Dump(br)
    go func() {
        for {
            fmt.Println(br)
            spew.Dump(br)
        }
    }()

    return nil
}

Является ли Go единственным способом добиться этого, или я могу сделать то же самое и в Python?
Это мой первый проект с Django кстати ..
Спасибо за помощь.

...