portainer/api/http/docker_handler.go

111 lines
2.8 KiB
Go
Raw Normal View History

2016-12-18 05:21:29 +00:00
package http
import (
"strconv"
2016-12-18 05:21:29 +00:00
"github.com/portainer/portainer"
"log"
"net/http"
"net/url"
"os"
"github.com/gorilla/mux"
2016-12-18 05:21:29 +00:00
)
// DockerHandler represents an HTTP API handler for proxying requests to the Docker API.
type DockerHandler struct {
*mux.Router
Logger *log.Logger
EndpointService portainer.EndpointService
ProxyFactory ProxyFactory
proxies map[portainer.EndpointID]http.Handler
2016-12-18 05:21:29 +00:00
}
// NewDockerHandler returns a new instance of DockerHandler.
func NewDockerHandler(mw *middleWareService, resourceControlService portainer.ResourceControlService) *DockerHandler {
2016-12-18 05:21:29 +00:00
h := &DockerHandler{
Router: mux.NewRouter(),
Logger: log.New(os.Stderr, "", log.LstdFlags),
ProxyFactory: ProxyFactory{
ResourceControlService: resourceControlService,
},
proxies: make(map[portainer.EndpointID]http.Handler),
2016-12-18 05:21:29 +00:00
}
h.PathPrefix("/{id}/").Handler(
mw.authenticated(http.HandlerFunc(h.proxyRequestsToDockerAPI)))
2016-12-18 05:21:29 +00:00
return h
}
func checkEndpointAccessControl(endpoint *portainer.Endpoint, userID portainer.UserID) bool {
for _, authorizedUserID := range endpoint.AuthorizedUsers {
if authorizedUserID == userID {
return true
2016-12-18 05:21:29 +00:00
}
}
return false
2016-12-18 05:21:29 +00:00
}
func (handler *DockerHandler) proxyRequestsToDockerAPI(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
parsedID, err := strconv.Atoi(id)
if err != nil {
Error(w, err, http.StatusBadRequest, handler.Logger)
return
}
endpointID := portainer.EndpointID(parsedID)
endpoint, err := handler.EndpointService.Endpoint(endpointID)
if err != nil {
Error(w, err, http.StatusInternalServerError, handler.Logger)
return
}
2016-12-18 05:21:29 +00:00
tokenData, err := extractTokenDataFromRequestContext(r)
2016-12-18 05:21:29 +00:00
if err != nil {
Error(w, err, http.StatusInternalServerError, handler.Logger)
2016-12-18 05:21:29 +00:00
}
if tokenData.Role != portainer.AdministratorRole && !checkEndpointAccessControl(endpoint, tokenData.ID) {
Error(w, portainer.ErrEndpointAccessDenied, http.StatusForbidden, handler.Logger)
return
2016-12-18 05:21:29 +00:00
}
proxy := handler.proxies[endpointID]
if proxy == nil {
proxy, err = handler.createAndRegisterEndpointProxy(endpoint)
if err != nil {
Error(w, err, http.StatusBadRequest, handler.Logger)
return
}
}
http.StripPrefix("/"+id, proxy).ServeHTTP(w, r)
2016-12-18 05:21:29 +00:00
}
func (handler *DockerHandler) createAndRegisterEndpointProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
var proxy http.Handler
2016-12-18 05:21:29 +00:00
endpointURL, err := url.Parse(endpoint.URL)
2016-12-18 05:21:29 +00:00
if err != nil {
return nil, err
2016-12-18 05:21:29 +00:00
}
if endpointURL.Scheme == "tcp" {
if endpoint.TLS {
proxy, err = handler.ProxyFactory.newHTTPSProxy(endpointURL, endpoint)
if err != nil {
return nil, err
}
} else {
proxy = handler.ProxyFactory.newHTTPProxy(endpointURL)
2016-12-18 05:21:29 +00:00
}
} else {
// Assume unix:// scheme
proxy = handler.ProxyFactory.newSocketProxy(endpointURL.Path)
2016-12-18 05:21:29 +00:00
}
handler.proxies[endpoint.ID] = proxy
return proxy, nil
2016-12-18 05:21:29 +00:00
}