Support BYOK for OSS storage driver

Change-Id: I423ad03e63bd38aded3abfcba49079ff2fbb3b74
Signed-off-by: Li Yi <denverdino@gmail.com>
This commit is contained in:
Li Yi 2018-12-25 08:30:40 +08:00
parent 40b7b5830a
commit 90bed67126
15 changed files with 1216 additions and 110 deletions

View file

@ -0,0 +1,88 @@
package oss
import (
"crypto"
"crypto/md5"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"io/ioutil"
"net/http"
"regexp"
"strings"
"sync"
)
type authenticationType struct {
lock *sync.RWMutex
certificate map[string]*rsa.PublicKey
}
var (
authentication = authenticationType{lock: &sync.RWMutex{}, certificate: map[string]*rsa.PublicKey{}}
urlReg = regexp.MustCompile(`^http(|s)://gosspublic.alicdn.com/[0-9a-zA-Z]`)
)
//验证OSS向业务服务器发来的回调函数。
//该方法是并发安全的
//pubKeyUrl 回调请求头中[x-oss-pub-key-url]一项以Base64编码
//reqUrl oss所发来请求的url由path+query组成
//reqBody oss所发来请求的body
//authorization authorization为回调头中的签名
func AuthenticateCallBack(pubKeyUrl, reqUrl, reqBody, authorization string) error {
//获取证书url
keyURL, err := base64.URLEncoding.DecodeString(pubKeyUrl)
if err != nil {
return err
}
url := string(keyURL)
//判断证书是否来自于阿里云
if !urlReg.Match(keyURL) {
return errors.New("certificate address error")
}
//获取文件名
rs := []rune(url)
filename := string(rs[strings.LastIndex(url, "/") : len(rs)-1])
authentication.lock.RLock()
certificate := authentication.certificate[filename]
authentication.lock.RUnlock()
//内存中没有证书,下载
if certificate == nil {
authentication.lock.Lock()
res, err := http.Get(url)
if err != nil {
return err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
block, _ := pem.Decode(body)
if block == nil {
return errors.New("certificate error")
}
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return err
}
certificate = pubKey.(*rsa.PublicKey)
authentication.certificate[filename] = certificate
authentication.lock.Unlock()
}
//证书准备完毕,开始验证
//解析签名
signature, err := base64.StdEncoding.DecodeString(authorization)
if err != nil {
return err
}
hashed := md5.New()
hashed.Write([]byte(reqUrl + "\n" + reqBody))
if err := rsa.VerifyPKCS1v15(certificate, crypto.MD5, hashed.Sum(nil), signature); err != nil {
return err
}
//验证通过
return nil
}

View file

@ -57,12 +57,14 @@ type Owner struct {
// Options struct
//
type Options struct {
ServerSideEncryption bool
Meta map[string][]string
ContentEncoding string
CacheControl string
ContentMD5 string
ContentDisposition string
ServerSideEncryption bool
ServerSideEncryptionKeyID string
Meta map[string][]string
ContentEncoding string
CacheControl string
ContentMD5 string
ContentDisposition string
//Range string
//Expires int
}
@ -72,6 +74,9 @@ type CopyOptions struct {
CopySourceOptions string
MetadataDirective string
//ContentType string
ServerSideEncryption bool
ServerSideEncryptionKeyID string
}
// CopyObjectResult is the output from a Copy request
@ -366,7 +371,7 @@ func (b *Bucket) Exists(path string) (exists bool, err error) {
}
if err != nil {
// We can treat a 403 or 404 as non existance
// We can treat a 403 or 404 as non existence
if e, ok := err.(*Error); ok && (e.StatusCode == 403 || e.StatusCode == 404) {
return false, nil
}
@ -430,7 +435,7 @@ func (b *Bucket) Put(path string, data []byte, contType string, perm ACL, option
func (b *Bucket) PutCopy(path string, perm ACL, options CopyOptions, source string) (*CopyObjectResult, error) {
headers := make(http.Header)
headers.Set("x-oss-acl", string(perm))
headers.Set("x-oss-object-acl", string(perm))
headers.Set("x-oss-copy-source", source)
options.addHeaders(headers)
@ -455,7 +460,7 @@ func (b *Bucket) PutReader(path string, r io.Reader, length int64, contType stri
headers := make(http.Header)
headers.Set("Content-Length", strconv.FormatInt(length, 10))
headers.Set("Content-Type", contType)
headers.Set("x-oss-acl", string(perm))
headers.Set("x-oss-object-acl", string(perm))
options.addHeaders(headers)
req := &request{
@ -491,7 +496,10 @@ func (b *Bucket) PutFile(path string, file *os.File, perm ACL, options Options)
// addHeaders adds o's specified fields to headers
func (o Options) addHeaders(headers http.Header) {
if o.ServerSideEncryption {
if len(o.ServerSideEncryptionKeyID) != 0 {
headers.Set("x-oss-server-side-encryption", "KMS")
headers.Set("x-oss-server-side-encryption-key-id", o.ServerSideEncryptionKeyID)
} else if o.ServerSideEncryption {
headers.Set("x-oss-server-side-encryption", "AES256")
}
if len(o.ContentEncoding) != 0 {
@ -516,6 +524,13 @@ func (o Options) addHeaders(headers http.Header) {
// addHeaders adds o's specified fields to headers
func (o CopyOptions) addHeaders(headers http.Header) {
if len(o.ServerSideEncryptionKeyID) != 0 {
headers.Set("x-oss-server-side-encryption", "KMS")
headers.Set("x-oss-server-side-encryption-key-id", o.ServerSideEncryptionKeyID)
} else if o.ServerSideEncryption {
headers.Set("x-oss-server-side-encryption", "AES256")
}
if len(o.MetadataDirective) != 0 {
headers.Set("x-oss-metadata-directive", o.MetadataDirective)
}
@ -1100,7 +1115,7 @@ func (client *Client) setupHttpRequest(req *request) (*http.Request, error) {
// body will be unmarshalled on it.
func (client *Client) doHttpRequest(c *http.Client, hreq *http.Request, resp interface{}) (*http.Response, error) {
if true {
if client.debug {
log.Printf("%s %s ...\n", hreq.Method, hreq.URL.String())
}
hresp, err := c.Do(hreq)
@ -1323,9 +1338,9 @@ func (b *Bucket) CopyLargeFileInParallel(sourcePath string, destPath string, con
}
currentLength, err := b.GetContentLength(sourcePath)
log.Printf("Parallel Copy large file[size: %d] from %s to %s\n",currentLength, sourcePath, destPath)
log.Printf("Parallel Copy large file[size: %d] from %s to %s\n", currentLength, sourcePath, destPath)
if err != nil {
return err
}

View file

@ -9,15 +9,25 @@ type Region string
// Constants of region definition
const (
Hangzhou = Region("oss-cn-hangzhou")
Qingdao = Region("oss-cn-qingdao")
Beijing = Region("oss-cn-beijing")
Hongkong = Region("oss-cn-hongkong")
Shenzhen = Region("oss-cn-shenzhen")
Hangzhou = Region("oss-cn-hangzhou")
Qingdao = Region("oss-cn-qingdao")
Beijing = Region("oss-cn-beijing")
Hongkong = Region("oss-cn-hongkong")
Shenzhen = Region("oss-cn-shenzhen")
Shanghai = Region("oss-cn-shanghai")
Zhangjiakou = Region("oss-cn-zhangjiakou")
Huhehaote = Region("oss-cn-huhehaote")
USWest1 = Region("oss-us-west-1")
USEast1 = Region("oss-us-east-1")
APSouthEast1 = Region("oss-ap-southeast-1")
Shanghai = Region("oss-cn-shanghai")
APNorthEast1 = Region("oss-ap-northeast-1")
APSouthEast2 = Region("oss-ap-southeast-2")
MEEast1 = Region("oss-me-east-1")
EUCentral1 = Region("oss-eu-central-1")
EUWest1 = Region("oss-eu-west-1")
DefaultRegion = Hangzhou
)