1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
19 "git.arvados.org/arvados.git/sdk/go/arvados"
20 "git.arvados.org/arvados.git/sdk/go/ctxlog"
21 "git.arvados.org/arvados.git/sdk/go/httpserver"
24 // ContainerSSH returns a connection to the SSH server in the
25 // appropriate crunch-run process on the worker node where the
26 // specified container is running.
28 // If the returned error is nil, the caller is responsible for closing
30 func (conn *Conn) ContainerSSH(ctx context.Context, opts arvados.ContainerSSHOptions) (sshconn arvados.ContainerSSHConnection, err error) {
31 ctr, err := conn.railsProxy.ContainerGet(ctx, arvados.GetOptions{UUID: opts.UUID})
35 if ctr.GatewayAddress == "" || ctr.State != arvados.ContainerStateRunning {
36 err = httpserver.ErrorWithStatus(fmt.Errorf("gateway is not available, container is %s", strings.ToLower(string(ctr.State))), http.StatusBadGateway)
39 netconn, err := net.Dial("tcp", ctr.GatewayAddress)
43 bufr := bufio.NewReader(netconn)
44 bufw := bufio.NewWriter(netconn)
46 // Note this auth header does not protect from replay/mitm
47 // attacks (TODO: use TLS for that). It only authenticates us
49 h := hmac.New(sha256.New, []byte(conn.cluster.SystemRootToken))
50 fmt.Fprint(h, "%s", opts.UUID)
51 auth := fmt.Sprintf("%x", h.Sum(nil))
55 Host: ctr.GatewayAddress,
58 bufw.WriteString("GET " + u.String() + " HTTP/1.1\r\n")
59 bufw.WriteString("Host: " + u.Host + "\r\n")
60 bufw.WriteString("Upgrade: ssh\r\n")
61 bufw.WriteString("X-Arvados-Target-Uuid: " + opts.UUID + "\r\n")
62 bufw.WriteString("X-Arvados-Authorization: " + auth + "\r\n")
63 bufw.WriteString("X-Arvados-Detach-Keys: " + opts.DetachKeys + "\r\n")
64 bufw.WriteString("X-Arvados-Login-Username: " + opts.LoginUsername + "\r\n")
65 bufw.WriteString("\r\n")
67 resp, err := http.ReadResponse(bufr, &http.Request{Method: "GET"})
69 err = fmt.Errorf("error reading http response from gateway: %w", err)
73 if strings.ToLower(resp.Header.Get("Upgrade")) != "ssh" ||
74 strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
75 err = httpserver.ErrorWithStatus(errors.New("bad upgrade"), http.StatusBadGateway)
79 sshconn.Conn = netconn
80 sshconn.Bufrw = &bufio.ReadWriter{Reader: bufr, Writer: bufw}
81 sshconn.Logger = ctxlog.FromContext(ctx)