mirror of
https://github.com/harness/drone.git
synced 2025-05-04 15:31:26 +08:00
226 lines
6.1 KiB
Go
226 lines
6.1 KiB
Go
// Source: https://github.com/pivotal-golang/s3cli
|
|
|
|
// Copyright (c) 2013 Damien Le Berrigaud and Nick Wade
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
package s3
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"net/http"
|
|
"net/url"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
"github.com/aws/aws-sdk-go/aws/request"
|
|
"github.com/aws/aws-sdk-go/service/s3"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type signer struct {
|
|
// Values that must be populated from the request.
|
|
Request *http.Request
|
|
Time time.Time
|
|
Credentials *credentials.Credentials
|
|
Query url.Values
|
|
stringToSign string
|
|
signature string
|
|
}
|
|
|
|
var s3ParamsToSign = map[string]bool{
|
|
"acl": true,
|
|
"location": true,
|
|
"logging": true,
|
|
"notification": true,
|
|
"partNumber": true,
|
|
"policy": true,
|
|
"requestPayment": true,
|
|
"torrent": true,
|
|
"uploadId": true,
|
|
"uploads": true,
|
|
"versionId": true,
|
|
"versioning": true,
|
|
"versions": true,
|
|
"response-content-type": true,
|
|
"response-content-language": true,
|
|
"response-expires": true,
|
|
"response-cache-control": true,
|
|
"response-content-disposition": true,
|
|
"response-content-encoding": true,
|
|
"website": true,
|
|
"delete": true,
|
|
}
|
|
|
|
// setv2Handlers will setup v2 signature signing on the S3 driver.
|
|
func setv2Handlers(svc *s3.S3) {
|
|
svc.Handlers.Build.PushBack(
|
|
func(r *request.Request) {
|
|
parsedURL, err := url.Parse(r.HTTPRequest.URL.String())
|
|
if err != nil {
|
|
log.Fatal().Msgf("Failed to parse URL: %v", err)
|
|
}
|
|
r.HTTPRequest.URL.Opaque = parsedURL.Path
|
|
},
|
|
)
|
|
|
|
svc.Handlers.Sign.Clear()
|
|
svc.Handlers.Sign.PushBack(Sign)
|
|
svc.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
|
|
}
|
|
|
|
// Sign requests with signature version 2.
|
|
//
|
|
// Will sign the requests with the service config's Credentials object
|
|
// Signing is skipped if the credentials is the credentials.AnonymousCredentials
|
|
// object.
|
|
func Sign(req *request.Request) {
|
|
// If the request does not need to be signed ignore the signing of the
|
|
// request if the AnonymousCredentials object is used.
|
|
if req.Config.Credentials == credentials.AnonymousCredentials {
|
|
return
|
|
}
|
|
|
|
v2 := signer{
|
|
Request: req.HTTPRequest,
|
|
Time: req.Time,
|
|
Credentials: req.Config.Credentials,
|
|
}
|
|
|
|
// nolint:errcheck.
|
|
err := v2.Sign()
|
|
if err != nil {
|
|
log.Fatal().Msgf("Error in signing s3: %v", err)
|
|
}
|
|
}
|
|
|
|
func (v2 *signer) Sign() error {
|
|
credValue, err := v2.Credentials.Get()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
accessKey := credValue.AccessKeyID
|
|
var (
|
|
md5, ctype, date, xamz string
|
|
xamzDate bool
|
|
sarray []string
|
|
smap map[string]string
|
|
sharray []string
|
|
)
|
|
|
|
headers := v2.Request.Header
|
|
params := v2.Request.URL.Query()
|
|
parsedURL, err := url.Parse(v2.Request.URL.String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
host, canonicalPath := parsedURL.Host, parsedURL.Path
|
|
v2.Request.Header["Host"] = []string{host}
|
|
v2.Request.Header["date"] = []string{v2.Time.In(time.UTC).Format(time.RFC1123)}
|
|
if credValue.SessionToken != "" {
|
|
v2.Request.Header["x-amz-security-token"] = []string{credValue.SessionToken}
|
|
}
|
|
|
|
smap = make(map[string]string)
|
|
for k, v := range headers {
|
|
k = strings.ToLower(k)
|
|
switch k {
|
|
case "content-md5":
|
|
md5 = v[0]
|
|
case "content-type":
|
|
ctype = v[0]
|
|
case "date":
|
|
if !xamzDate {
|
|
date = v[0]
|
|
}
|
|
default:
|
|
if strings.HasPrefix(k, "x-amz-") {
|
|
vall := strings.Join(v, ",")
|
|
smap[k] = k + ":" + vall
|
|
if k == "x-amz-date" {
|
|
xamzDate = true
|
|
date = ""
|
|
}
|
|
sharray = append(sharray, k)
|
|
}
|
|
}
|
|
}
|
|
if len(sharray) > 0 {
|
|
sort.StringSlice(sharray).Sort()
|
|
for _, h := range sharray {
|
|
sarray = append(sarray, smap[h])
|
|
}
|
|
xamz = strings.Join(sarray, "\n") + "\n"
|
|
}
|
|
|
|
expires := false
|
|
if v, ok := params["Expires"]; ok {
|
|
expires = true
|
|
date = v[0]
|
|
params["AWSAccessKeyId"] = []string{accessKey}
|
|
}
|
|
|
|
sarray = sarray[0:0]
|
|
for k, v := range params {
|
|
if s3ParamsToSign[k] {
|
|
for _, vi := range v {
|
|
if vi == "" {
|
|
sarray = append(sarray, k)
|
|
} else {
|
|
sarray = append(sarray, k+"="+vi)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if len(sarray) > 0 {
|
|
sort.StringSlice(sarray).Sort()
|
|
canonicalPath = canonicalPath + "?" + strings.Join(sarray, "&")
|
|
}
|
|
|
|
v2.stringToSign = strings.Join(
|
|
[]string{
|
|
v2.Request.Method,
|
|
md5,
|
|
ctype,
|
|
date,
|
|
xamz + canonicalPath,
|
|
}, "\n",
|
|
)
|
|
hash := hmac.New(sha256.New, []byte(credValue.SecretAccessKey))
|
|
hash.Write([]byte(v2.stringToSign))
|
|
v2.signature = base64.StdEncoding.EncodeToString(hash.Sum(nil))
|
|
|
|
if expires {
|
|
params["Signature"] = []string{v2.signature}
|
|
} else {
|
|
headers["Authorization"] = []string{"AWS " + accessKey + ":" + v2.signature}
|
|
}
|
|
|
|
log.Debug().
|
|
Interface("string-to-sign", v2.stringToSign).
|
|
Interface("signature", v2.signature).
|
|
Msg("request signature")
|
|
return nil
|
|
}
|