X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/7c47ef8311a4cd801fe22b3428a991285b5442a2..HEAD:/lib/controller/handler.go diff --git a/lib/controller/handler.go b/lib/controller/handler.go index 9054dd6330..7c4bb0912f 100644 --- a/lib/controller/handler.go +++ b/lib/controller/handler.go @@ -140,6 +140,8 @@ func (h *Handler) setup() { mux.Handle("/arvados/v1/groups/", rtr) mux.Handle("/arvados/v1/links", rtr) mux.Handle("/arvados/v1/links/", rtr) + mux.Handle("/arvados/v1/authorized_keys", rtr) + mux.Handle("/arvados/v1/authorized_keys/", rtr) mux.Handle("/login", rtr) mux.Handle("/logout", rtr) mux.Handle("/arvados/v1/api_client_authorizations", rtr) @@ -147,6 +149,7 @@ func (h *Handler) setup() { hs := http.NotFoundHandler() hs = prepend(hs, h.proxyRailsAPI) + hs = prepend(hs, h.routeContainerEndpoints(rtr)) hs = prepend(hs, h.limitLogCreateRequests) hs = h.setupProxyRemoteCluster(hs) hs = prepend(hs, oidcAuthorizer.Middleware) @@ -202,9 +205,32 @@ func (h *Handler) localClusterRequest(req *http.Request) (*http.Response, error) if insecure { client = h.insecureClient } + // Clearing the Host field here causes the Go http client to + // use the host part of urlOut as the Host header in the + // outgoing request, instead of the Host value from the + // original request we received. + req.Host = "" return h.proxy.Do(req, urlOut, client) } +// Route /arvados/v1/containers/{uuid}/log*, .../ssh, and +// .../gateway_tunnel to rtr, pass everything else to next. +// +// (http.ServeMux doesn't let us route these without also routing +// everything under /containers/, which we don't want yet.) +func (h *Handler) routeContainerEndpoints(rtr http.Handler) middlewareFunc { + return func(w http.ResponseWriter, req *http.Request, next http.Handler) { + trim := strings.TrimPrefix(req.URL.Path, "/arvados/v1/containers/") + if trim != req.URL.Path && (strings.Index(trim, "/log") == 27 || + strings.Index(trim, "/ssh") == 27 || + strings.Index(trim, "/gateway_tunnel") == 27) { + rtr.ServeHTTP(w, req) + } else { + next.ServeHTTP(w, req) + } + } +} + func (h *Handler) limitLogCreateRequests(w http.ResponseWriter, req *http.Request, next http.Handler) { if cap(h.limitLogCreate) > 0 && req.Method == http.MethodPost && strings.HasPrefix(req.URL.Path, "/arvados/v1/logs") { select { @@ -226,11 +252,15 @@ type cacheEnt struct { mtx sync.Mutex header http.Header body []byte + expireAfter time.Time refreshAfter time.Time refreshLock sync.Mutex } -const cacheTTL = 5 * time.Minute +const ( + cacheTTL = 5 * time.Minute + cacheExpire = 24 * time.Hour +) func (ent *cacheEnt) refresh(path string, do func(*http.Request) (*http.Response, error)) (http.Header, []byte, error) { ent.refreshLock.Lock() @@ -254,13 +284,15 @@ func (ent *cacheEnt) refresh(path string, do func(*http.Request) (*http.Response ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute)) defer cancel() - // 0.0.0.0:0 is just a placeholder here -- do(), which is + // "http://localhost" is just a placeholder here -- we'll fill + // in req.URL.Path below, and then do(), which is // localClusterRequest(), will replace the scheme and host // parts with the real proxy destination. - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://0.0.0.0:0/"+path, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost", nil) if err != nil { return nil, nil, err } + req.URL.Path = path resp, err := do(req) if err != nil { return nil, nil, err @@ -292,12 +324,16 @@ func (ent *cacheEnt) refresh(path string, do func(*http.Request) (*http.Response ent.header = header ent.body = body ent.refreshAfter = time.Now().Add(cacheTTL) + ent.expireAfter = time.Now().Add(cacheExpire) return ent.header, ent.body, nil } func (ent *cacheEnt) response() (http.Header, []byte, bool) { ent.mtx.Lock() defer ent.mtx.Unlock() + if ent.expireAfter.Before(time.Now()) { + ent.header, ent.body, ent.refreshAfter = nil, nil, time.Time{} + } return ent.header, ent.body, ent.refreshAfter.Before(time.Now()) }