Enhancement: compatible with older version s3 credential

Added compatibility with 1.5 version of credential information for
signature verification and policy check of s3 interface.

In version 1.5, each volume has its own access key, which is stored
in the view information center of the volume to which it belongs.

In version 2.0, user security and authorization are added. Each user
has their own access key, and users can use this access key to operate
all their own volumes.

Increase the compatibility with the old version authentication method
to reduce the impact of online version 1.5 cluster upgrade to 2.0 on
online services.

Signed-off-by: Mofei Zhang <mofei2816@gmail.com>
This commit is contained in:
Mofei Zhang 2020-04-19 21:24:10 +08:00
parent 26248fd42f
commit 43561c35b0
No known key found for this signature in database
GPG Key ID: 7C19725C197AE672
4 changed files with 132 additions and 39 deletions

View File

@ -177,17 +177,35 @@ func (o *ObjectNode) validateHeaderBySignatureAlgorithmV2(r *http.Request) (bool
return false, err
}
var userInfo *proto.UserInfo
if userInfo, err = o.getUserInfoByAccessKey(authInfo.accessKeyId); err != nil {
log.LogInfof("get secretKey from master error: accessKey(%v), err(%v)", authInfo.accessKeyId, err)
var accessKey = authInfo.accessKeyId
var volume *Volume
if bucket := mux.Vars(r)["bucket"]; len(bucket) > 0 {
volume, _ = o.getVol(bucket)
}
var secretKey string
if userInfo, err := o.getUserInfoByAccessKey(accessKey); err == nil {
secretKey = userInfo.SecretKey
} else if (err == proto.ErrUserNotExists || err == proto.ErrAccessKeyNotExists) && volume != nil {
// In order to be directly compatible with the signature verification of version 1.5
// (each volume has its own access key and secret key), if the user does not exist and
// the request specifies a volume, try to use the access key and secret key bound in the
// volume information for verification.
if ak, sk := volume.OSSSecure(); ak == accessKey {
secretKey = sk
} else {
return false, nil
}
} else {
log.LogErrorf("validateHeaderBySignatureAlgorithmV4: get secretKey from master fail: accessKey(%v) err(%v)",
accessKey, err)
return false, err
}
// 2. calculate new signature
newSignature, err1 := calculateSignatureV2(authInfo, userInfo.SecretKey, o.wildcards)
if err1 != nil {
newSignature, err := calculateSignatureV2(authInfo, secretKey, o.wildcards)
if err != nil {
log.LogInfof("calculute SignatureV2 error: %v, %v", authInfo.r, err)
return false, err1
return false, err
}
// 3. compare newSignatrue and reqSignature
@ -252,8 +270,6 @@ func calculateSignatureV2(authInfo *requestAuthInfoV2, secretKey string, wildcar
func (o *ObjectNode) validateUrlBySignatureAlgorithmV2(r *http.Request) (bool, error) {
var err error
uris := strings.SplitN(r.RequestURI, "?", 2)
if len(uris) < 2 {
log.LogInfof("validateUrlBySignatureAlgorithmV2 error, request url invalid %v ", r.RequestURI)
@ -273,10 +289,27 @@ func (o *ObjectNode) validateUrlBySignatureAlgorithmV2(r *http.Request) (bool, e
log.LogDebugf("validateUrlBySignatureAlgorithmV2: parse signature info: requestID(%v) url(%v) accessKey(%v) signature(%v) expires(%v)",
GetRequestID(r), r.URL.String(), accessKey, signature, expires)
//check access key
var userInfo *proto.UserInfo
if userInfo, err = o.getUserInfoByAccessKey(accessKey); err != nil {
log.LogInfof("get secretKey from master error: accessKey(%v), err(%v)", accessKey, err)
// Checking access key
var volume *Volume
if bucket := mux.Vars(r)["bucket"]; len(bucket) > 0 {
volume, _ = o.getVol(bucket)
}
var secretKey string
if userInfo, err := o.getUserInfoByAccessKey(accessKey); err == nil {
secretKey = userInfo.SecretKey
} else if (err == proto.ErrUserNotExists || err == proto.ErrAccessKeyNotExists) && volume != nil {
// In order to be directly compatible with the signature verification of version 1.5
// (each volume has its own access key and secret key), if the user does not exist and
// the request specifies a volume, try to use the access key and secret key bound in the
// volume information for verification.
if ak, sk := volume.OSSSecure(); ak == accessKey {
secretKey = sk
} else {
return false, nil
}
} else {
log.LogErrorf("validateHeaderBySignatureAlgorithmV4: get secretKey from master fail: accessKey(%v) err(%v)",
accessKey, err)
return false, err
}
@ -290,7 +323,7 @@ func (o *ObjectNode) validateUrlBySignatureAlgorithmV2(r *http.Request) (bool, e
var canonicalResource string
canonicalResource = getCanonicalizedResourceV2(r, o.wildcards)
canonicalResourceQuery := getCanonicalQueryV2(canonicalResource, r.URL.Query().Encode())
calSignature := calPresignedSignatureV2(r.Method, canonicalResourceQuery, expires, userInfo.SecretKey, r.Header)
calSignature := calPresignedSignatureV2(r.Method, canonicalResourceQuery, expires, secretKey, r.Header)
if calSignature != signature {
log.LogDebugf("validateUrlBySignatureAlgorithmV2: invalid signature: requestID(%v) client(%v) server(%v)",
GetRequestID(r), signature, calSignature)

View File

@ -100,12 +100,32 @@ func (o *ObjectNode) validateHeaderBySignatureAlgorithmV4(r *http.Request) (bool
if err != nil {
return false, err
}
var userInfo *proto.UserInfo
if userInfo, err = o.getUserInfoByAccessKey(req.Credential.AccessKey); err != nil {
log.LogInfof("validateHeaderBySignatureAlgorithmV4: get secretKey from master fail: err(%v)", err)
var accessKey = req.Credential.AccessKey
var volume *Volume
if bucket := mux.Vars(r)["bucket"]; len(bucket) > 0 {
volume, _ = o.getVol(bucket)
}
var secretKey string
if userInfo, err := o.getUserInfoByAccessKey(accessKey); err == nil {
secretKey = userInfo.SecretKey
} else if (err == proto.ErrUserNotExists || err == proto.ErrAccessKeyNotExists) && volume != nil {
// In order to be directly compatible with the signature verification of version 1.5
// (each volume has its own access key and secret key), if the user does not exist and
// the request specifies a volume, try to use the accesskey and secret key bound in the
// volume information for verification.
if ak, sk := volume.OSSSecure(); ak == accessKey {
secretKey = sk
} else {
return false, nil
}
} else {
log.LogErrorf("validateHeaderBySignatureAlgorithmV4: get secretKey from master fail: accessKey(%v) err(%v)",
accessKey, err)
return false, err
}
newSignature := calculateSignatureV4(r, req.Credential.Region, userInfo.SecretKey, req.SignedHeaders)
newSignature := calculateSignatureV4(r, req.Credential.Region, secretKey, req.SignedHeaders)
if req.Signature != newSignature {
log.LogDebugf("validateHeaderBySignatureAlgorithmV4: invalid signature: requestID(%v) client(%v) server(%v)",
GetRequestID(r), req.Signature, newSignature)
@ -139,12 +159,30 @@ func (o *ObjectNode) validateUrlBySignatureAlgorithmV4(r *http.Request) (pass bo
return
}
// check accessKey valid
var userInfo *proto.UserInfo
if userInfo, err = o.getUserInfoByAccessKey(req.Credential.AccessKey); err != nil {
log.LogInfof("get secretKey from master error: accessKey(%v), err(%v)", req.Credential.AccessKey, err)
var accessKey = req.Credential.AccessKey
var volume *Volume
if bucket := mux.Vars(r)["bucket"]; len(bucket) > 0 {
volume, _ = o.getVol(bucket)
}
var secretKey string
if userInfo, err := o.getUserInfoByAccessKey(accessKey); err == nil {
secretKey = userInfo.SecretKey
} else if (err == proto.ErrUserNotExists || err == proto.ErrAccessKeyNotExists) && volume != nil {
// In order to be directly compatible with the signature verification of version 1.5
// (each volume has its own access key and secret key), if the user does not exist and
// the request specifies a volume, try to use the accesskey and secret key bound in the
// volume information for verification.
if ak, sk := volume.OSSSecure(); ak == accessKey {
secretKey = sk
} else {
return false, nil
}
} else {
log.LogErrorf("validateHeaderBySignatureAlgorithmV4: get secretKey from master fail: accessKey(%v) err(%v)",
accessKey, err)
return false, err
}
// create canonicalRequest
var canonicalHeader http.Header
canonicalHeader, err = req.createCanonicalHeaderV4()
@ -165,7 +203,7 @@ func (o *ObjectNode) validateUrlBySignatureAlgorithmV4(r *http.Request) (pass bo
canonicalRequestString)
// build signingKey
signingKey := buildSigningKey(SCHEME, userInfo.SecretKey, req.Credential.Date, req.Credential.Region, req.Credential.Service, req.Credential.Request)
signingKey := buildSigningKey(SCHEME, secretKey, req.Credential.Date, req.Credential.Region, req.Credential.Service, req.Credential.Request)
// build stringToSign
scope := buildScope(req.Credential.Date, req.Credential.Region, req.Credential.Service, req.Credential.Request)

View File

@ -26,6 +26,8 @@ import (
"net/http"
"strings"
"github.com/gorilla/mux"
"github.com/chubaofs/chubaofs/proto"
"github.com/chubaofs/chubaofs/util/log"
)
@ -212,26 +214,37 @@ func (o *ObjectNode) policyCheck(f http.HandlerFunc) http.HandlerFunc {
}
// Check user policy
var volume *Volume
if bucket := mux.Vars(r)["bucket"]; len(bucket) > 0 {
volume, _ = o.getVol(bucket)
}
var userInfo *proto.UserInfo
if userInfo, err = o.getUserInfoByAccessKey(param.accessKey); err != nil {
if userInfo, err = o.getUserInfoByAccessKey(param.AccessKey()); err == nil {
// White list for admin and root user.
if userInfo.UserType == proto.UserTypeRoot || userInfo.UserType == proto.UserTypeAdmin {
log.LogDebugf("policyCheck: user is admin: requestID(%v) userID(%v) accessKey(%v) volume(%v)",
GetRequestID(r), userInfo.UserID, param.AccessKey(), param.Bucket())
allowed = true
return
}
var userPolicy = userInfo.Policy
if !userPolicy.IsOwn(param.Bucket()) && !userPolicy.IsAuthorized(param.Bucket(), param.Action()) {
log.LogDebugf("policyCheck: user no permission: requestID(%v) userID(%v) accessKey(%v) volume(%v) action(%v)",
GetRequestID(r), userInfo.UserID, param.AccessKey(), param.Bucket(), param.Action())
allowed = false
return
}
} else if (err == proto.ErrAccessKeyNotExists || err == proto.ErrUserNotExists) && volume != nil {
if ak, _ := volume.OSSSecure(); ak != param.AccessKey() {
allowed = false
return
}
} else {
log.LogErrorf("policyCheck: load user policy from master fail: requestID(%v) accessKey(%v) err(%v)",
GetRequestID(r), param.AccessKey(), err)
allowed = false
return
}
if userInfo.UserType == proto.UserTypeRoot || userInfo.UserType == proto.UserTypeAdmin {
log.LogDebugf("policyCheck: user is admin: requestID(%v) userID(%v) accessKey(%v) volume(%v)",
GetRequestID(r), userInfo.UserID, param.AccessKey(), param.Bucket())
allowed = true
return
}
var userPolicy = userInfo.Policy
if !userPolicy.IsOwn(param.Bucket()) && !userPolicy.IsAuthorized(param.Bucket(), param.Action()) {
log.LogDebugf("policyCheck: user no permission: requestID(%v) userID(%v) accessKey(%v) volume(%v) action(%v)",
GetRequestID(r), userInfo.UserID, param.AccessKey(), param.Bucket(), param.Action())
allowed = false
return
}
var vol *Volume
var acl *AccessControlPolicy
@ -255,7 +268,7 @@ func (o *ObjectNode) policyCheck(f http.HandlerFunc) http.HandlerFunc {
allowed = policy.IsAllowed(param)
if !allowed {
log.LogWarnf("policyCheck: bucket policy not allowed: requestID(%v) userID(%v) accessKey(%v) volume(%v) action(%v)",
GetRequestID(r), userInfo.UserID, param.AccessKey(), param.Bucket(), param.Action())
GetRequestID(r), userInfo, param.AccessKey(), param.Bucket(), param.Action())
return
}
}
@ -264,13 +277,13 @@ func (o *ObjectNode) policyCheck(f http.HandlerFunc) http.HandlerFunc {
allowed = acl.IsAllowed(param)
if !allowed {
log.LogWarnf("policyCheck: bucket ACL not allowed: requestID(%v) userID(%v) accessKey(%v) volume(%v) action(%v)",
GetRequestID(r), userInfo.UserID, param.AccessKey(), param.Bucket(), param.Action())
GetRequestID(r), userInfo, param.AccessKey(), param.Bucket(), param.Action())
return
}
}
allowed = true
log.LogDebugf("policyCheck: action allowed: requestID(%v) userID(%v) accessKey(%v) volume(%v) action(%v)",
GetRequestID(r), userInfo.UserID, param.AccessKey(), param.Bucket(), param.Action())
GetRequestID(r), userInfo, param.AccessKey(), param.Bucket(), param.Action())
}
}

View File

@ -15,6 +15,7 @@
package proto
import (
"fmt"
"regexp"
"sync"
)
@ -101,6 +102,14 @@ type UserInfo struct {
CreateTime string `json:"create_time"`
}
func (i *UserInfo) String() string {
if i == nil {
return "nil"
}
return fmt.Sprintf("%v_%v_%v_%v",
i.UserID, i.AccessKey, i.SecretKey, i.UserType)
}
func NewUserInfo() *UserInfo {
return &UserInfo{Policy: NewUserPolicy()}
}