1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
19 "git.curoverse.com/arvados.git/sdk/go/arvados"
20 "git.curoverse.com/arvados.git/sdk/go/arvadostest"
21 "git.curoverse.com/arvados.git/sdk/go/auth"
22 "git.curoverse.com/arvados.git/sdk/go/keepclient"
23 check "gopkg.in/check.v1"
26 var _ = check.Suite(&ProxyRemoteSuite{})
28 type ProxyRemoteSuite struct {
29 cluster *arvados.Cluster
33 remoteClusterID string
34 remoteBlobSigningKey []byte
35 remoteKeepLocator string
37 remoteKeepproxy *httptest.Server
38 remoteKeepRequests int64
39 remoteAPI *httptest.Server
42 func (s *ProxyRemoteSuite) remoteKeepproxyHandler(w http.ResponseWriter, r *http.Request) {
43 expectToken, err := auth.SaltToken(arvadostest.ActiveTokenV2, s.remoteClusterID)
47 atomic.AddInt64(&s.remoteKeepRequests, 1)
49 if auth := strings.Split(r.Header.Get("Authorization"), " "); len(auth) == 2 && (auth[0] == "OAuth2" || auth[0] == "Bearer") {
52 if r.Method == "GET" && r.URL.Path == "/"+s.remoteKeepLocator && token == expectToken {
53 w.Write(s.remoteKeepData)
56 http.Error(w, "404", 404)
59 func (s *ProxyRemoteSuite) remoteAPIHandler(w http.ResponseWriter, r *http.Request) {
60 host, port, _ := net.SplitHostPort(strings.Split(s.remoteKeepproxy.URL, "//")[1])
61 portnum, _ := strconv.Atoi(port)
62 if r.URL.Path == "/arvados/v1/discovery/v1/rest" {
63 json.NewEncoder(w).Encode(arvados.DiscoveryDocument{})
66 if r.URL.Path == "/arvados/v1/keep_services/accessible" {
67 json.NewEncoder(w).Encode(arvados.KeepServiceList{
68 Items: []arvados.KeepService{
70 UUID: s.remoteClusterID + "-bi6l4-proxyproxyproxy",
74 ServiceSSLFlag: false,
80 http.Error(w, "404", 404)
83 func (s *ProxyRemoteSuite) SetUpTest(c *check.C) {
84 s.remoteClusterID = "z0000"
85 s.remoteBlobSigningKey = []byte("3b6df6fb6518afe12922a5bc8e67bf180a358bc8")
86 s.remoteKeepproxy = httptest.NewServer(http.HandlerFunc(s.remoteKeepproxyHandler))
87 s.remoteAPI = httptest.NewUnstartedServer(http.HandlerFunc(s.remoteAPIHandler))
88 s.remoteAPI.StartTLS()
89 s.cluster = arvados.IntegrationTestCluster()
90 s.cluster.RemoteClusters = map[string]arvados.RemoteCluster{
91 s.remoteClusterID: arvados.RemoteCluster{
92 Host: strings.Split(s.remoteAPI.URL, "//")[1],
98 s.vm = MakeTestVolumeManager(2)
100 theConfig = DefaultConfig()
101 theConfig.systemAuthToken = arvadostest.DataManagerToken
102 theConfig.blobSigningKey = []byte(knownKey)
104 s.rtr = MakeRESTRouter(s.cluster)
107 func (s *ProxyRemoteSuite) TearDownTest(c *check.C) {
110 theConfig = DefaultConfig()
113 s.remoteKeepproxy.Close()
116 func (s *ProxyRemoteSuite) TestProxyRemote(c *check.C) {
117 data := []byte("foo bar")
118 s.remoteKeepData = data
119 locator := fmt.Sprintf("%x+%d", md5.Sum(data), len(data))
120 s.remoteKeepLocator = keepclient.SignLocator(locator, arvadostest.ActiveTokenV2, time.Now().Add(time.Minute), time.Minute, s.remoteBlobSigningKey)
122 path := "/" + strings.Replace(s.remoteKeepLocator, "+A", "+R"+s.remoteClusterID+"-", 1)
124 for _, trial := range []struct {
128 xKeepSignature string
129 expectRemoteReqs int64
136 token: arvadostest.ActiveTokenV2,
138 expectCode: http.StatusOK,
141 label: "obsolete token",
143 token: arvadostest.ActiveToken,
145 expectCode: http.StatusBadRequest,
150 token: arvadostest.ActiveTokenV2[:len(arvadostest.ActiveTokenV2)-3] + "xxx",
152 expectCode: http.StatusNotFound,
157 token: arvadostest.ActiveTokenV2,
159 expectCode: http.StatusOK,
162 label: "HEAD with local signature",
164 xKeepSignature: "local, time=" + time.Now().Format(time.RFC3339),
165 token: arvadostest.ActiveTokenV2,
167 expectCode: http.StatusOK,
168 expectSignature: true,
171 label: "GET with local signature",
173 xKeepSignature: "local, time=" + time.Now().Format(time.RFC3339),
174 token: arvadostest.ActiveTokenV2,
176 expectCode: http.StatusOK,
177 expectSignature: true,
180 c.Logf("trial: %s", trial.label)
182 s.remoteKeepRequests = 0
184 var req *http.Request
185 var resp *httptest.ResponseRecorder
186 req = httptest.NewRequest(trial.method, path, nil)
187 req.Header.Set("Authorization", "Bearer "+trial.token)
188 if trial.xKeepSignature != "" {
189 req.Header.Set("X-Keep-Signature", trial.xKeepSignature)
191 resp = httptest.NewRecorder()
192 s.rtr.ServeHTTP(resp, req)
193 c.Check(s.remoteKeepRequests, check.Equals, trial.expectRemoteReqs)
194 c.Check(resp.Code, check.Equals, trial.expectCode)
195 if resp.Code == http.StatusOK {
196 c.Check(resp.Body.String(), check.Equals, string(data))
198 c.Check(resp.Body.String(), check.Not(check.Equals), string(data))
201 c.Check(resp.Header().Get("Vary"), check.Matches, `(.*, )?X-Keep-Signature(, .*)?`)
203 locHdr := resp.Header().Get("X-Keep-Locator")
204 if !trial.expectSignature {
205 c.Check(locHdr, check.Equals, "")
209 c.Check(locHdr, check.Not(check.Equals), "")
210 c.Check(locHdr, check.Not(check.Matches), `.*\+R.*`)
211 c.Check(VerifySignature(locHdr, trial.token), check.IsNil)
213 // Ensure block can be requested using new signature
214 req = httptest.NewRequest("GET", "/"+locHdr, nil)
215 req.Header.Set("Authorization", "Bearer "+trial.token)
216 resp = httptest.NewRecorder()
217 s.rtr.ServeHTTP(resp, req)
218 c.Check(resp.Code, check.Equals, http.StatusOK)
219 c.Check(s.remoteKeepRequests, check.Equals, trial.expectRemoteReqs)