20647: Fix duplicate response headers via reverse proxy.
[arvados.git] / lib / controller / localdb / container_gateway.go
index 11d139663e528a1fe3ec02c6df6dbe63c908c865..e42a4473084a943f57af4d2699da96e169206eed 100644 (file)
@@ -31,6 +31,7 @@ import (
        "git.arvados.org/arvados.git/sdk/go/auth"
        "git.arvados.org/arvados.git/sdk/go/ctxlog"
        "git.arvados.org/arvados.git/sdk/go/httpserver"
+       keepweb "git.arvados.org/arvados.git/services/keep-web"
        "github.com/hashicorp/yamux"
        "golang.org/x/net/webdav"
 )
@@ -78,6 +79,16 @@ var (
 // ...or the request may be handled locally using an empty-collection
 // stub.
 func (conn *Conn) ContainerRequestLog(ctx context.Context, opts arvados.ContainerLogOptions) (http.Handler, error) {
+       if opts.Method == "OPTIONS" && opts.Header.Get("Access-Control-Request-Method") != "" {
+               return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+                       if !keepweb.ServeCORSPreflight(w, opts.Header) {
+                               // Inconceivable.  We already checked
+                               // for the only condition where
+                               // ServeCORSPreflight returns false.
+                               httpserver.Error(w, "unhandled CORS preflight request", http.StatusInternalServerError)
+                       }
+               }), nil
+       }
        cr, err := conn.railsProxy.ContainerRequestGet(ctx, arvados.GetOptions{UUID: opts.UUID, Select: []string{"uuid", "container_uuid", "log_uuid"}})
        if err != nil {
                if se := httpserver.HTTPStatusError(nil); errors.As(err, &se) && se.HTTPStatus() == http.StatusUnauthorized {
@@ -172,6 +183,15 @@ func (conn *Conn) ContainerRequestLog(ctx context.Context, opts arvados.Containe
                                        // an attacker-in-the-middle.
                                        return httpserver.ErrorWithStatus(errors.New("bad X-Arvados-Authorization-Response header"), http.StatusBadGateway)
                                }
+                               resp.Header.Del("X-Arvados-Authorization-Response")
+                               for hdr := range resp.Header {
+                                       // proxy.ServeHTTP adds each
+                                       // resp.Header to w.Header,
+                                       // which causes duplicate CORS
+                                       // and request-id headers,
+                                       // unless we do this.
+                                       w.Header().Del(hdr)
+                               }
                                return nil
                        },
                        ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
@@ -220,7 +240,7 @@ func (conn *Conn) serveContainerRequestLogViaKeepWeb(opts arvados.ContainerLogOp
        myHostname := u.Hostname()
        var webdavBase arvados.URL
        var ok bool
-       for webdavBase = range conn.cluster.Services.WebDAVDownload.InternalURLs {
+       for webdavBase = range conn.cluster.Services.WebDAV.InternalURLs {
                ok = true
                u := url.URL(webdavBase)
                if h := u.Hostname(); h == "127.0.0.1" || h == "0.0.0.0" || h == "::1" || h == myHostname {
@@ -262,6 +282,17 @@ func (conn *Conn) serveContainerRequestLogViaKeepWeb(opts arvados.ContainerLogOp
                                r.Header.Set("X-Webdav-Source", "/log for container "+opts.Path[1:28]+"/")
                        }
                },
+               ModifyResponse: func(resp *http.Response) error {
+                       for hdr := range resp.Header {
+                               // proxy.ServeHTTP adds each
+                               // resp.Header to w.Header, which
+                               // causes duplicate CORS and
+                               // request-id headers, unless we do
+                               // this.
+                               w.Header().Del(hdr)
+                       }
+                       return nil
+               },
        }
        if conn.cluster.TLS.Insecure {
                proxy.Transport = &http.Transport{