X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/fed1d9f5e7a442abb6b2e86114e8c3d05cff5329..60a72f91316ab1d878420adceb98e585c0e3b72f:/lib/controller/localdb/container_gateway.go diff --git a/lib/controller/localdb/container_gateway.go b/lib/controller/localdb/container_gateway.go index f6eaea6d96..3b40eccaff 100644 --- a/lib/controller/localdb/container_gateway.go +++ b/lib/controller/localdb/container_gateway.go @@ -18,6 +18,7 @@ import ( "strings" "git.arvados.org/arvados.git/sdk/go/arvados" + "git.arvados.org/arvados.git/sdk/go/auth" "git.arvados.org/arvados.git/sdk/go/ctxlog" "git.arvados.org/arvados.git/sdk/go/httpserver" ) @@ -29,12 +30,48 @@ import ( // If the returned error is nil, the caller is responsible for closing // sshconn.Conn. func (conn *Conn) ContainerSSH(ctx context.Context, opts arvados.ContainerSSHOptions) (sshconn arvados.ContainerSSHConnection, err error) { + user, err := conn.railsProxy.UserGetCurrent(ctx, arvados.GetOptions{}) + if err != nil { + return + } ctr, err := conn.railsProxy.ContainerGet(ctx, arvados.GetOptions{UUID: opts.UUID}) if err != nil { return } - if ctr.GatewayAddress == "" || ctr.State != arvados.ContainerStateRunning { - err = httpserver.ErrorWithStatus(fmt.Errorf("gateway is not available, container is %s", strings.ToLower(string(ctr.State))), http.StatusBadGateway) + ctxRoot := auth.NewContext(ctx, &auth.Credentials{Tokens: []string{conn.cluster.SystemRootToken}}) + if !user.IsAdmin || !conn.cluster.Containers.ShellAccess.Admin { + if !conn.cluster.Containers.ShellAccess.User { + err = httpserver.ErrorWithStatus(errors.New("shell access is disabled in config"), http.StatusServiceUnavailable) + return + } + var crs arvados.ContainerRequestList + crs, err = conn.railsProxy.ContainerRequestList(ctxRoot, arvados.ListOptions{Limit: -1, Filters: []arvados.Filter{{"container_uuid", "=", opts.UUID}}}) + if err != nil { + return + } + for _, cr := range crs.Items { + if cr.ModifiedByUserUUID != user.UUID { + err = httpserver.ErrorWithStatus(errors.New("permission denied: container is associated with requests submitted by other users"), http.StatusForbidden) + return + } + } + if crs.ItemsAvailable != len(crs.Items) { + err = httpserver.ErrorWithStatus(errors.New("incomplete response while checking permission"), http.StatusInternalServerError) + return + } + } + + switch ctr.State { + case arvados.ContainerStateQueued, arvados.ContainerStateLocked: + err = httpserver.ErrorWithStatus(fmt.Errorf("container is not running yet (state is %q)", ctr.State), http.StatusServiceUnavailable) + return + case arvados.ContainerStateRunning: + if ctr.GatewayAddress == "" { + err = httpserver.ErrorWithStatus(errors.New("container is running but gateway is not available -- installation problem or feature not supported"), http.StatusServiceUnavailable) + return + } + default: + err = httpserver.ErrorWithStatus(fmt.Errorf("container has ended (state is %q)", ctr.State), http.StatusGone) return } // crunch-run uses a self-signed / unverifiable TLS @@ -75,6 +112,7 @@ func (conn *Conn) ContainerSSH(ctx context.Context, opts arvados.ContainerSSHOpt }, }) if err != nil { + err = httpserver.ErrorWithStatus(err, http.StatusBadGateway) return } if respondAuth == "" { @@ -115,6 +153,20 @@ func (conn *Conn) ContainerSSH(ctx context.Context, opts arvados.ContainerSSHOpt netconn.Close() return } + + if !ctr.InteractiveSessionStarted { + _, err = conn.railsProxy.ContainerUpdate(ctxRoot, arvados.UpdateOptions{ + UUID: opts.UUID, + Attrs: map[string]interface{}{ + "interactive_session_started": true, + }, + }) + if err != nil { + netconn.Close() + return + } + } + sshconn.Conn = netconn sshconn.Bufrw = &bufio.ReadWriter{Reader: bufr, Writer: bufw} sshconn.Logger = ctxlog.FromContext(ctx)