"encoding/xml"
"errors"
"fmt"
+ "hash"
"io"
"net/http"
"net/url"
return h.Sum(nil)
}
+func hashdigest(h hash.Hash, payload string) string {
+ io.WriteString(h, payload)
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
+
// Signing key for given secret key and request attrs.
func s3signatureKey(key, datestamp, regionName, serviceName string) []byte {
return hmacstring("aws4_request",
return strings.Join(keys, "&")
}
-func s3signature(alg, secretKey, scope, signedHeaders string, r *http.Request) (string, error) {
+func s3stringToSign(alg, scope, signedHeaders string, r *http.Request) (string, error) {
timefmt, timestr := "20060102T150405Z", r.Header.Get("X-Amz-Date")
if timestr == "" {
timefmt, timestr = time.RFC1123, r.Header.Get("Date")
var canonicalHeaders string
for _, h := range strings.Split(signedHeaders, ";") {
if h == "host" {
- canonicalHeaders += h + ":" + r.URL.Host + "\n"
+ canonicalHeaders += h + ":" + r.Host + "\n"
} else {
canonicalHeaders += h + ":" + r.Header.Get(h) + "\n"
}
}
- crhash := sha256.New()
- fmt.Fprintf(crhash, "%s\n%s\n%s\n%s\n%s\n%s", r.Method, r.URL.EscapedPath(), s3querystring(r.URL), canonicalHeaders, signedHeaders, r.Header.Get("X-Amz-Content-Sha256"))
- crdigest := fmt.Sprintf("%x", crhash.Sum(nil))
-
- payload := fmt.Sprintf("%s\n%s\n%s\n%s", alg, r.Header.Get("X-Amz-Date"), scope, crdigest)
+ canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", r.Method, r.URL.EscapedPath(), s3querystring(r.URL), canonicalHeaders, signedHeaders, r.Header.Get("X-Amz-Content-Sha256"))
+ ctxlog.FromContext(r.Context()).Debugf("s3stringToSign: canonicalRequest %s", canonicalRequest)
+ return fmt.Sprintf("%s\n%s\n%s\n%s", alg, r.Header.Get("X-Amz-Date"), scope, hashdigest(sha256.New(), canonicalRequest)), nil
+}
+func s3signature(secretKey, scope, signedHeaders, stringToSign string) (string, error) {
// scope is {datestamp}/{region}/{service}/aws4_request
drs := strings.Split(scope, "/")
if len(drs) != 4 {
return "", fmt.Errorf("invalid scope %q", scope)
}
-
key := s3signatureKey(secretKey, drs[0], drs[1], drs[2])
- h := hmac.New(sha256.New, key)
- h.Write([]byte(payload))
- return fmt.Sprintf("%x", h.Sum(nil)), nil
+ return hashdigest(hmac.New(sha256.New, key), stringToSign), nil
}
// checks3signature verifies the given S3 V4 signature and returns the
Insecure: h.Config.cluster.TLS.Insecure,
}).WithRequestID(r.Header.Get("X-Request-Id"))
var aca arvados.APIClientAuthorization
- ctx := arvados.ContextWithAuthorization(r.Context(), "Bearer "+h.Config.cluster.SystemRootToken)
- err := client.RequestAndDecodeContext(ctx, &aca, "GET", "arvados/v1/api_client_authorizations/"+key, nil, nil)
+ var secret string
+ var err error
+ if len(key) == 27 && key[5:12] == "-gj3su-" {
+ // Access key is the UUID of an Arvados token, secret
+ // key is the secret part.
+ ctx := arvados.ContextWithAuthorization(r.Context(), "Bearer "+h.Config.cluster.SystemRootToken)
+ err = client.RequestAndDecodeContext(ctx, &aca, "GET", "arvados/v1/api_client_authorizations/"+key, nil, nil)
+ secret = aca.APIToken
+ } else {
+ // Access key and secret key are both an entire
+ // Arvados token or OIDC access token.
+ ctx := arvados.ContextWithAuthorization(r.Context(), "Bearer "+key)
+ err = client.RequestAndDecodeContext(ctx, &aca, "GET", "arvados/v1/api_client_authorizations/current", nil, nil)
+ secret = key
+ }
if err != nil {
- ctxlog.FromContext(ctx).WithError(err).WithField("UUID", key).Info("token lookup failed")
+ ctxlog.FromContext(r.Context()).WithError(err).WithField("UUID", key).Info("token lookup failed")
return "", errors.New("invalid access key")
}
- expect, err := s3signature(s3SignAlgorithm, aca.APIToken, scope, signedHeaders, r)
+ stringToSign, err := s3stringToSign(s3SignAlgorithm, scope, signedHeaders, r)
+ if err != nil {
+ return "", err
+ }
+ expect, err := s3signature(secret, scope, signedHeaders, stringToSign)
if err != nil {
return "", err
} else if expect != signature {
- return "", errors.New("signature does not match")
+ return "", fmt.Errorf("signature does not match (scope %q signedHeaders %q stringToSign %q)", scope, signedHeaders, stringToSign)
}
- return aca.TokenV2(), nil
+ return secret, nil
}
// serveS3 handles r and returns true if r is a request from an S3
// github.com/aws/aws-sdk-net never terminates its
// paging loop).
NextMarker string `xml:"NextMarker,omitempty"`
+ // ListObjectsV2 has a KeyCount response field.
+ KeyCount int
}
resp := listResp{
ListResp: s3.ListResp{
}
sort.Slice(resp.CommonPrefixes, func(i, j int) bool { return resp.CommonPrefixes[i].Prefix < resp.CommonPrefixes[j].Prefix })
}
+ resp.KeyCount = len(resp.Contents)
w.Header().Set("Content-Type", "application/xml")
io.WriteString(w, xml.Header)
if err := xml.NewEncoder(w).Encode(resp); err != nil {