2021-08-05 03:02:06 +00:00
package websocket
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
bolterrors "github.com/portainer/portainer/api/bolt/errors"
"github.com/portainer/portainer/api/http/security"
)
2021-10-07 02:32:07 +00:00
// @summary Execute a websocket on kubectl shell pod
// @description The request will be upgraded to the websocket protocol. The request will proxy input from the client to the pod via long-lived websocket connection.
// @description **Access policy**: authenticated
2021-11-30 02:31:16 +00:00
// @security ApiKeyAuth
2021-10-07 02:32:07 +00:00
// @security jwt
// @tags websocket
// @accept json
// @produce json
// @param endpointId query int true "environment(endpoint) ID of the environment(endpoint) where the resource is located"
// @param token query string true "JWT token used for authentication against this environment(endpoint)"
2021-10-11 23:12:08 +00:00
// @success 200 "Success"
// @failure 400 "Invalid request"
// @failure 403 "Permission denied"
// @failure 500 "Server error"
2021-10-07 02:32:07 +00:00
// @router /websocket/kubernetes-shell [get]
2021-08-05 03:02:06 +00:00
func ( handler * Handler ) websocketShellPodExec ( w http . ResponseWriter , r * http . Request ) * httperror . HandlerError {
endpointID , err := request . RetrieveNumericQueryParameter ( r , "endpointId" , false )
if err != nil {
return & httperror . HandlerError { http . StatusBadRequest , "Invalid query parameter: endpointId" , err }
}
endpoint , err := handler . DataStore . Endpoint ( ) . Endpoint ( portainer . EndpointID ( endpointID ) )
if err == bolterrors . ErrObjectNotFound {
2021-09-08 08:42:17 +00:00
return & httperror . HandlerError { http . StatusNotFound , "Unable to find the environment associated to the stack inside the database" , err }
2021-08-05 03:02:06 +00:00
} else if err != nil {
2021-09-08 08:42:17 +00:00
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to find the environment associated to the stack inside the database" , err }
2021-08-05 03:02:06 +00:00
}
tokenData , err := security . RetrieveTokenData ( r )
if err != nil {
2021-09-08 08:42:17 +00:00
return & httperror . HandlerError { http . StatusForbidden , "Permission denied to access environment" , err }
2021-08-05 03:02:06 +00:00
}
cli , err := handler . KubernetesClientFactory . GetKubeClient ( endpoint )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to create Kubernetes client" , err }
}
serviceAccount , err := cli . GetServiceAccount ( tokenData )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to find serviceaccount associated with user" , err }
}
2021-09-28 22:39:45 +00:00
settings , err := handler . DataStore . Settings ( ) . Settings ( )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable read settings" , err }
}
shellPod , err := cli . CreateUserShellPod ( r . Context ( ) , serviceAccount . Name , settings . KubectlShellImage )
2021-08-05 03:02:06 +00:00
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to create user shell" , err }
}
// Modifying request params mid-flight before forewarding to K8s API server (websocket)
q := r . URL . Query ( )
q . Add ( "namespace" , shellPod . Namespace )
q . Add ( "podName" , shellPod . PodName )
q . Add ( "containerName" , shellPod . ContainerName )
q . Add ( "command" , shellPod . ShellExecCommand )
r . URL . RawQuery = q . Encode ( )
// Modify url path mid-flight before forewarding to k8s API server (websocket)
r . URL . Path = "/websocket/pod"
/ *
Note : The following websocket proxying logic is duplicated from ` api/http/handler/websocket/pod.go `
* /
params := & webSocketRequestParams {
endpoint : endpoint ,
}
r . Header . Del ( "Origin" )
if endpoint . Type == portainer . AgentOnKubernetesEnvironment {
err := handler . proxyAgentWebsocketRequest ( w , r , params )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to proxy websocket request to agent" , err }
}
return nil
} else if endpoint . Type == portainer . EdgeAgentOnKubernetesEnvironment {
err := handler . proxyEdgeAgentWebsocketRequest ( w , r , params )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to proxy websocket request to Edge agent" , err }
}
return nil
}
handlerErr := handler . hijackPodExecStartOperation (
w ,
r ,
cli ,
2021-08-25 23:31:22 +00:00
"" ,
true ,
2021-08-05 03:02:06 +00:00
endpoint ,
shellPod . Namespace ,
shellPod . PodName ,
shellPod . ContainerName ,
shellPod . ShellExecCommand ,
)
if handlerErr != nil {
return handlerErr
}
return nil
}