17464: Add upload/download logging and permissions to keepproxy
authorPeter Amstutz <peter.amstutz@curii.com>
Wed, 16 Jun 2021 20:03:48 +0000 (16:03 -0400)
committerPeter Amstutz <peter.amstutz@curii.com>
Fri, 18 Jun 2021 15:35:01 +0000 (11:35 -0400)
Add tests.

Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz@curii.com>

services/keepproxy/keepproxy.go
services/keepproxy/keepproxy_test.go

index 538a0612275ec029e448b810f45bcdd08fee74bb..df6e06a74111580b6ad2c2e7bcfdd48bc5067ea3 100644 (file)
@@ -163,19 +163,20 @@ func run(logger log.FieldLogger, cluster *arvados.Cluster) error {
        signal.Notify(term, syscall.SIGINT)
 
        // Start serving requests.
-       router = MakeRESTRouter(kc, time.Duration(keepclient.DefaultProxyRequestTimeout), cluster.ManagementToken)
+       router = MakeRESTRouter(kc, time.Duration(keepclient.DefaultProxyRequestTimeout), cluster, logger)
        return http.Serve(listener, httpserver.AddRequestIDs(httpserver.LogRequests(router)))
 }
 
 type APITokenCache struct {
        tokens     map[string]int64
+       tokenUser  map[string]*arvados.User
        lock       sync.Mutex
        expireTime int64
 }
 
 // RememberToken caches the token and set an expire time.  If we already have
 // an expire time on the token, it is not updated.
-func (cache *APITokenCache) RememberToken(token string) {
+func (cache *APITokenCache) RememberToken(token string, user *arvados.User) {
        cache.lock.Lock()
        defer cache.lock.Unlock()
 
@@ -183,25 +184,26 @@ func (cache *APITokenCache) RememberToken(token string) {
        if cache.tokens[token] == 0 {
                cache.tokens[token] = now + cache.expireTime
        }
+       cache.tokenUser[token] = user
 }
 
 // RecallToken checks if the cached token is known and still believed to be
 // valid.
-func (cache *APITokenCache) RecallToken(token string) bool {
+func (cache *APITokenCache) RecallToken(token string) (bool, *arvados.User) {
        cache.lock.Lock()
        defer cache.lock.Unlock()
 
        now := time.Now().Unix()
        if cache.tokens[token] == 0 {
                // Unknown token
-               return false
+               return false, nil
        } else if now < cache.tokens[token] {
                // Token is known and still valid
-               return true
+               return true, cache.tokenUser[token]
        } else {
                // Token is expired
                cache.tokens[token] = 0
-               return false
+               return false, nil
        }
 }
 
@@ -216,10 +218,10 @@ func GetRemoteAddress(req *http.Request) string {
        return req.RemoteAddr
 }
 
-func CheckAuthorizationHeader(kc *keepclient.KeepClient, cache *APITokenCache, req *http.Request) (pass bool, tok string) {
+func (h *proxyHandler) CheckAuthorizationHeader(req *http.Request) (pass bool, tok string, user *arvados.User) {
        parts := strings.SplitN(req.Header.Get("Authorization"), " ", 2)
        if len(parts) < 2 || !(parts[0] == "OAuth2" || parts[0] == "Bearer") || len(parts[1]) == 0 {
-               return false, ""
+               return false, "", nil
        }
        tok = parts[1]
 
@@ -234,29 +236,52 @@ func CheckAuthorizationHeader(kc *keepclient.KeepClient, cache *APITokenCache, r
                op = "write"
        }
 
-       if cache.RecallToken(op + ":" + tok) {
+       if ok, user := h.APITokenCache.RecallToken(op + ":" + tok); ok {
                // Valid in the cache, short circuit
-               return true, tok
+               return true, tok, user
        }
 
        var err error
-       arv := *kc.Arvados
+       arv := *h.KeepClient.Arvados
        arv.ApiToken = tok
        arv.RequestID = req.Header.Get("X-Request-Id")
+       user = &arvados.User{}
+       userCurrentError := arv.Call("GET", "users", "", "current", nil, user)
        if op == "read" {
+               // scoped token this will fail the user current check,
+               // but if it is a download operation and they can read
+               // the keep_services table, it's okay.
                err = arv.Call("HEAD", "keep_services", "", "accessible", nil, nil)
        } else {
-               err = arv.Call("HEAD", "users", "", "current", nil, nil)
+               err = userCurrentError
        }
        if err != nil {
                log.Printf("%s: CheckAuthorizationHeader error: %v", GetRemoteAddress(req), err)
-               return false, ""
+               return false, "", nil
+       }
+
+       if userCurrentError == nil && user.IsAdmin {
+               // checking userCurrentError is probably redundant,
+               // IsAdmin would be false anyway. But can't hurt.
+               if op == "read" && !h.cluster.Collections.KeepproxyPermission.Admin.Download {
+                       return false, "", nil
+               }
+               if op == "write" && !h.cluster.Collections.KeepproxyPermission.Admin.Upload {
+                       return false, "", nil
+               }
+       } else {
+               if op == "read" && !h.cluster.Collections.KeepproxyPermission.User.Download {
+                       return false, "", nil
+               }
+               if op == "write" && !h.cluster.Collections.KeepproxyPermission.User.Upload {
+                       return false, "", nil
+               }
        }
 
        // Success!  Update cache
-       cache.RememberToken(op + ":" + tok)
+       h.APITokenCache.RememberToken(op+":"+tok, user)
 
-       return true, tok
+       return true, tok, user
 }
 
 // We need to make a private copy of the default http transport early
@@ -273,11 +298,13 @@ type proxyHandler struct {
        *APITokenCache
        timeout   time.Duration
        transport *http.Transport
+       logger    log.FieldLogger
+       cluster   *arvados.Cluster
 }
 
 // MakeRESTRouter returns an http.Handler that passes GET and PUT
 // requests to the appropriate handlers.
-func MakeRESTRouter(kc *keepclient.KeepClient, timeout time.Duration, mgmtToken string) http.Handler {
+func MakeRESTRouter(kc *keepclient.KeepClient, timeout time.Duration, cluster *arvados.Cluster, logger log.FieldLogger) http.Handler {
        rest := mux.NewRouter()
 
        transport := defaultTransport
@@ -296,8 +323,11 @@ func MakeRESTRouter(kc *keepclient.KeepClient, timeout time.Duration, mgmtToken
                transport:  &transport,
                APITokenCache: &APITokenCache{
                        tokens:     make(map[string]int64),
+                       tokenUser:  make(map[string]*arvados.User),
                        expireTime: 300,
                },
+               logger:  logger,
+               cluster: cluster,
        }
 
        rest.HandleFunc(`/{locator:[0-9a-f]{32}\+.*}`, h.Get).Methods("GET", "HEAD")
@@ -316,7 +346,7 @@ func MakeRESTRouter(kc *keepclient.KeepClient, timeout time.Duration, mgmtToken
        rest.HandleFunc(`/`, h.Options).Methods("OPTIONS")
 
        rest.Handle("/_health/{check}", &health.Handler{
-               Token:  mgmtToken,
+               Token:  cluster.ManagementToken,
                Prefix: "/_health/",
        }).Methods("GET")
 
@@ -326,9 +356,9 @@ func MakeRESTRouter(kc *keepclient.KeepClient, timeout time.Duration, mgmtToken
 
 var errLoopDetected = errors.New("loop detected")
 
-func (*proxyHandler) checkLoop(resp http.ResponseWriter, req *http.Request) error {
+func (*proxyHandler) checkLoop(resp http.ResponseWriter, req *http.Request) error {
        if via := req.Header.Get("Via"); strings.Index(via, " "+viaAlias) >= 0 {
-               log.Printf("proxy loop detected (request has Via: %q): perhaps keepproxy is misidentified by gateway config as an external client, or its keep_services record does not have service_type=proxy?", via)
+               h.logger.Printf("proxy loop detected (request has Via: %q): perhaps keepproxy is misidentified by gateway config as an external client, or its keep_services record does not have service_type=proxy?", via)
                http.Error(resp, errLoopDetected.Error(), http.StatusInternalServerError)
                return errLoopDetected
        }
@@ -354,7 +384,7 @@ func (h *proxyHandler) Options(resp http.ResponseWriter, req *http.Request) {
        SetCorsHeaders(resp)
 }
 
-var errBadAuthorizationHeader = errors.New("Missing or invalid Authorization header")
+var errBadAuthorizationHeader = errors.New("Missing or invalid Authorization header, or method not allowed")
 var errContentLengthMismatch = errors.New("Actual length != expected content length")
 var errMethodNotSupported = errors.New("Method not supported")
 
@@ -384,7 +414,8 @@ func (h *proxyHandler) Get(resp http.ResponseWriter, req *http.Request) {
 
        var pass bool
        var tok string
-       if pass, tok = CheckAuthorizationHeader(kc, h.APITokenCache, req); !pass {
+       var user *arvados.User
+       if pass, tok, user = h.CheckAuthorizationHeader(req); !pass {
                status, err = http.StatusForbidden, errBadAuthorizationHeader
                return
        }
@@ -398,6 +429,18 @@ func (h *proxyHandler) Get(resp http.ResponseWriter, req *http.Request) {
 
        locator = removeHint.ReplaceAllString(locator, "$1")
 
+       if locator != "" {
+               parts := strings.SplitN(locator, "+", 3)
+               if len(parts) >= 2 {
+                       logger := h.logger
+                       if user != nil {
+                               logger = logger.WithField("user_uuid", user.UUID).
+                                       WithField("user_full_name", user.FullName)
+                       }
+                       logger.WithField("locator", fmt.Sprintf("%s+%s", parts[0], parts[1])).Infof("Block download")
+               }
+       }
+
        switch req.Method {
        case "HEAD":
                expectLength, proxiedURI, err = kc.Ask(locator)
@@ -498,7 +541,8 @@ func (h *proxyHandler) Put(resp http.ResponseWriter, req *http.Request) {
 
        var pass bool
        var tok string
-       if pass, tok = CheckAuthorizationHeader(kc, h.APITokenCache, req); !pass {
+       var user *arvados.User
+       if pass, tok, user = h.CheckAuthorizationHeader(req); !pass {
                err = errBadAuthorizationHeader
                status = http.StatusForbidden
                return
@@ -531,6 +575,18 @@ func (h *proxyHandler) Put(resp http.ResponseWriter, req *http.Request) {
                locatorOut, wroteReplicas, err = kc.PutHR(locatorIn, req.Body, expectLength)
        }
 
+       if locatorOut != "" {
+               parts := strings.SplitN(locatorOut, "+", 3)
+               if len(parts) >= 2 {
+                       logger := h.logger
+                       if user != nil {
+                               logger = logger.WithField("user_uuid", user.UUID).
+                                       WithField("user_full_name", user.FullName)
+                       }
+                       logger.WithField("locator", fmt.Sprintf("%s+%s", parts[0], parts[1])).Infof("Block upload")
+               }
+       }
+
        // Tell the client how many successful PUTs we accomplished
        resp.Header().Set(keepclient.XKeepReplicasStored, fmt.Sprintf("%d", wroteReplicas))
 
@@ -580,7 +636,7 @@ func (h *proxyHandler) Index(resp http.ResponseWriter, req *http.Request) {
        }()
 
        kc := h.makeKeepClient(req)
-       ok, token := CheckAuthorizationHeader(kc, h.APITokenCache, req)
+       ok, token, _ := h.CheckAuthorizationHeader(req)
        if !ok {
                status, err = http.StatusForbidden, errBadAuthorizationHeader
                return
index 6a02ab9bd3a8374dd5c7fed5888edd5c9a4217f8..9cf5ded89c50d140039a54faf7d5fbb96d912010 100644 (file)
@@ -120,7 +120,7 @@ func (s *NoKeepServerSuite) TearDownSuite(c *C) {
        arvadostest.StopAPI()
 }
 
-func runProxy(c *C, bogusClientToken bool, loadKeepstoresFromConfig bool) *keepclient.KeepClient {
+func runProxy(c *C, bogusClientToken bool, loadKeepstoresFromConfig bool, kp *arvados.UploadDownloadRolePermissions) (*keepclient.KeepClient, *bytes.Buffer) {
        cfg, err := config.NewLoader(nil, ctxlog.TestLogger(c)).Load()
        c.Assert(err, Equals, nil)
        cluster, err := cfg.GetCluster("")
@@ -133,9 +133,16 @@ func runProxy(c *C, bogusClientToken bool, loadKeepstoresFromConfig bool) *keepc
 
        cluster.Services.Keepproxy.InternalURLs = map[arvados.URL]arvados.ServiceInstance{{Host: ":0"}: {}}
 
+       if kp != nil {
+               cluster.Collections.KeepproxyPermission = *kp
+       }
+
        listener = nil
+       logbuf := &bytes.Buffer{}
+       logger := log.New()
+       logger.Out = logbuf
        go func() {
-               run(log.New(), cluster)
+               run(logger, cluster)
                defer closeListener()
        }()
        waitForListener()
@@ -153,11 +160,11 @@ func runProxy(c *C, bogusClientToken bool, loadKeepstoresFromConfig bool) *keepc
        kc.SetServiceRoots(sr, sr, sr)
        kc.Arvados.External = true
 
-       return kc
+       return kc, logbuf
 }
 
 func (s *ServerRequiredSuite) TestResponseViaHeader(c *C) {
-       runProxy(c, false, false)
+       runProxy(c, false, false, nil)
        defer closeListener()
 
        req, err := http.NewRequest("POST",
@@ -184,7 +191,7 @@ func (s *ServerRequiredSuite) TestResponseViaHeader(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestLoopDetection(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
 
        sr := map[string]string{
@@ -202,7 +209,7 @@ func (s *ServerRequiredSuite) TestLoopDetection(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestStorageClassesHeader(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
 
        // Set up fake keepstore to record request headers
@@ -229,7 +236,7 @@ func (s *ServerRequiredSuite) TestStorageClassesHeader(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestDesiredReplicas(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
 
        content := []byte("TestDesiredReplicas")
@@ -246,7 +253,7 @@ func (s *ServerRequiredSuite) TestDesiredReplicas(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPutWrongContentLength(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
 
        content := []byte("TestPutWrongContentLength")
@@ -257,7 +264,7 @@ func (s *ServerRequiredSuite) TestPutWrongContentLength(c *C) {
        // fixes the invalid Content-Length header. In order to test
        // our server behavior, we have to call the handler directly
        // using an httptest.ResponseRecorder.
-       rtr := MakeRESTRouter(kc, 10*time.Second, "")
+       rtr := MakeRESTRouter(kc, 10*time.Second, &arvados.Cluster{}, log.New())
 
        type testcase struct {
                sendLength   string
@@ -285,7 +292,7 @@ func (s *ServerRequiredSuite) TestPutWrongContentLength(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestManyFailedPuts(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
        router.(*proxyHandler).timeout = time.Nanosecond
 
@@ -312,7 +319,7 @@ func (s *ServerRequiredSuite) TestManyFailedPuts(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
-       kc := runProxy(c, false, false)
+       kc, logbuf := runProxy(c, false, false, nil)
        defer closeListener()
 
        hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
@@ -348,6 +355,9 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
                c.Check(rep, Equals, 2)
                c.Check(err, Equals, nil)
                c.Log("Finished PutB (expected success)")
+
+               c.Check(logbuf.String(), Matches, `(?ms).*msg="Block upload" locator=acbd18db4cc2f85cedef654fccc4a4d8\+3 user_full_name="TestCase Administrator" user_uuid=zzzzz-tpzed-d9tiejq69daie8f.*`)
+               logbuf.Reset()
        }
 
        {
@@ -355,6 +365,8 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
                c.Assert(err, Equals, nil)
                c.Check(blocklen, Equals, int64(3))
                c.Log("Finished Ask (expected success)")
+               c.Check(logbuf.String(), Matches, `(?ms).*msg="Block download" locator=acbd18db4cc2f85cedef654fccc4a4d8\+3 user_full_name="TestCase Administrator" user_uuid=zzzzz-tpzed-d9tiejq69daie8f.*`)
+               logbuf.Reset()
        }
 
        {
@@ -365,6 +377,8 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
                c.Check(all, DeepEquals, []byte("foo"))
                c.Check(blocklen, Equals, int64(3))
                c.Log("Finished Get (expected success)")
+               c.Check(logbuf.String(), Matches, `(?ms).*msg="Block download" locator=acbd18db4cc2f85cedef654fccc4a4d8\+3 user_full_name="TestCase Administrator" user_uuid=zzzzz-tpzed-d9tiejq69daie8f.*`)
+               logbuf.Reset()
        }
 
        {
@@ -389,7 +403,7 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) {
-       kc := runProxy(c, true, false)
+       kc, _ := runProxy(c, true, false, nil)
        defer closeListener()
 
        hash := fmt.Sprintf("%x+3", md5.Sum([]byte("bar")))
@@ -411,11 +425,109 @@ func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) {
        c.Check(err, FitsTypeOf, &keepclient.ErrNotFound{})
        c.Check(err, ErrorMatches, ".*not found.*")
        c.Check(blocklen, Equals, int64(0))
+}
+
+func testPermission(c *C, admin bool, perm arvados.UploadDownloadPermission) {
+       kp := arvados.UploadDownloadRolePermissions{}
+       if admin {
+               kp.Admin = perm
+               kp.User = arvados.UploadDownloadPermission{Upload: true, Download: true}
+       } else {
+               kp.Admin = arvados.UploadDownloadPermission{Upload: true, Download: true}
+               kp.User = perm
+       }
+
+       kc, logbuf := runProxy(c, false, false, &kp)
+       defer closeListener()
+       if admin {
+               kc.Arvados.ApiToken = arvadostest.AdminToken
+       } else {
+               kc.Arvados.ApiToken = arvadostest.ActiveToken
+       }
+
+       hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
+       var hash2 string
+
+       {
+               var rep int
+               var err error
+               hash2, rep, err = kc.PutB([]byte("foo"))
 
+               if perm.Upload {
+                       c.Check(hash2, Matches, fmt.Sprintf(`^%s\+3(\+.+)?$`, hash))
+                       c.Check(rep, Equals, 2)
+                       c.Check(err, Equals, nil)
+                       c.Log("Finished PutB (expected success)")
+                       if admin {
+                               c.Check(logbuf.String(), Matches, `(?ms).*msg="Block upload" locator=acbd18db4cc2f85cedef654fccc4a4d8\+3 user_full_name="TestCase Administrator" user_uuid=zzzzz-tpzed-d9tiejq69daie8f.*`)
+                       } else {
+
+                               c.Check(logbuf.String(), Matches, `(?ms).*msg="Block upload" locator=acbd18db4cc2f85cedef654fccc4a4d8\+3 user_full_name="Active User" user_uuid=zzzzz-tpzed-xurymjxw79nv3jz.*`)
+                       }
+               } else {
+                       c.Check(hash2, Equals, "")
+                       c.Check(rep, Equals, 0)
+                       c.Check(err, FitsTypeOf, keepclient.InsufficientReplicasError(errors.New("")))
+               }
+               logbuf.Reset()
+       }
+       if perm.Upload {
+               // can't test download without upload.
+
+               reader, blocklen, _, err := kc.Get(hash2)
+               if perm.Download {
+                       c.Assert(err, Equals, nil)
+                       all, err := ioutil.ReadAll(reader)
+                       c.Check(err, IsNil)
+                       c.Check(all, DeepEquals, []byte("foo"))
+                       c.Check(blocklen, Equals, int64(3))
+                       c.Log("Finished Get (expected success)")
+                       if admin {
+                               c.Check(logbuf.String(), Matches, `(?ms).*msg="Block download" locator=acbd18db4cc2f85cedef654fccc4a4d8\+3 user_full_name="TestCase Administrator" user_uuid=zzzzz-tpzed-d9tiejq69daie8f.*`)
+                       } else {
+                               c.Check(logbuf.String(), Matches, `(?ms).*msg="Block download" locator=acbd18db4cc2f85cedef654fccc4a4d8\+3 user_full_name="Active User" user_uuid=zzzzz-tpzed-xurymjxw79nv3jz.*`)
+                       }
+               } else {
+                       c.Check(err, FitsTypeOf, &keepclient.ErrNotFound{})
+                       c.Check(err, ErrorMatches, ".*Missing or invalid Authorization header, or method not allowed.*")
+                       c.Check(blocklen, Equals, int64(0))
+               }
+               logbuf.Reset()
+       }
+
+}
+
+func (s *ServerRequiredSuite) TestPutGetPermission(c *C) {
+
+       for _, adminperm := range []bool{true, false} {
+               for _, userperm := range []bool{true, false} {
+
+                       testPermission(c, true,
+                               arvados.UploadDownloadPermission{
+                                       Upload:   adminperm,
+                                       Download: true,
+                               })
+                       testPermission(c, true,
+                               arvados.UploadDownloadPermission{
+                                       Upload:   true,
+                                       Download: adminperm,
+                               })
+                       testPermission(c, false,
+                               arvados.UploadDownloadPermission{
+                                       Upload:   true,
+                                       Download: userperm,
+                               })
+                       testPermission(c, false,
+                               arvados.UploadDownloadPermission{
+                                       Upload:   true,
+                                       Download: userperm,
+                               })
+               }
+       }
 }
 
 func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
-       runProxy(c, false, false)
+       runProxy(c, false, false, nil)
        defer closeListener()
 
        {
@@ -446,7 +558,7 @@ func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPostWithoutHash(c *C) {
-       runProxy(c, false, false)
+       runProxy(c, false, false, nil)
        defer closeListener()
 
        {
@@ -504,7 +616,7 @@ func (s *ServerRequiredConfigYmlSuite) TestGetIndex(c *C) {
 }
 
 func getIndexWorker(c *C, useConfig bool) {
-       kc := runProxy(c, false, useConfig)
+       kc, _ := runProxy(c, false, useConfig, nil)
        defer closeListener()
 
        // Put "index-data" blocks
@@ -567,7 +679,7 @@ func getIndexWorker(c *C, useConfig bool) {
 }
 
 func (s *ServerRequiredSuite) TestCollectionSharingToken(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
        hash, _, err := kc.PutB([]byte("shareddata"))
        c.Check(err, IsNil)
@@ -580,7 +692,7 @@ func (s *ServerRequiredSuite) TestCollectionSharingToken(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPutAskGetInvalidToken(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
 
        // Put a test block
@@ -617,7 +729,7 @@ func (s *ServerRequiredSuite) TestPutAskGetInvalidToken(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestAskGetKeepProxyConnectionError(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
 
        // Point keepproxy at a non-existent keepstore
@@ -643,7 +755,7 @@ func (s *ServerRequiredSuite) TestAskGetKeepProxyConnectionError(c *C) {
 }
 
 func (s *NoKeepServerSuite) TestAskGetNoKeepServerError(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
 
        hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
@@ -666,10 +778,10 @@ func (s *NoKeepServerSuite) TestAskGetNoKeepServerError(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPing(c *C) {
-       kc := runProxy(c, false, false)
+       kc, _ := runProxy(c, false, false, nil)
        defer closeListener()
 
-       rtr := MakeRESTRouter(kc, 10*time.Second, arvadostest.ManagementToken)
+       rtr := MakeRESTRouter(kc, 10*time.Second, &arvados.Cluster{ManagementToken: arvadostest.ManagementToken}, log.New())
 
        req, err := http.NewRequest("GET",
                "http://"+listener.Addr().String()+"/_health/ping",