cubefs/objectnode/sts.go
yhjiango 4401624f96 feat(objectnode): s3 sts and signature auth
1. sts federation token
2. signature auth code refactor

Signed-off-by: yhjiango <jiangyunhua@oppo.com>
2023-08-30 20:05:32 +08:00

176 lines
4.5 KiB
Go

// Copyright 2023 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package objectnode
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"encoding/xml"
"strconv"
"strings"
"time"
"github.com/cubefs/cubefs/proto"
)
const (
stsAkPrefix = "STS"
stsSep = ";;sts;;"
stsActionKey = "Action"
stsActionValue = "GetFederationToken"
stsPolicyKey = "Policy"
stsNameKey = "Name"
stsDurationSecondsKey = "DurationSeconds"
)
type FederationTokenResponse struct {
XMLName *xml.Name `xml:"GetFederationTokenResponse"`
GetFederationTokenResult *FederationTokenResult `xml:"GetFederationTokenResult"`
ResponseMetadata struct {
RequestID string `xml:"RequestId,omitempty"`
} `xml:"ResponseMetadata,omitempty"`
}
type FederationTokenResult struct {
Credentials *FederatedCredentials `xml:"Credentials"`
FederatedUser *FederatedUser `xml:"FederatedUser"`
PackedPolicySize int `xml:",omitempty"`
}
type FederatedUser struct {
Arn string `xml:"Arn"`
FederatedUserId string `xml:"FederatedUserId"`
}
type FederatedCredentials struct {
AccessKeyId string `xml:"AccessKeyId"`
SecretAccessKey string `xml:"SecretAccessKey"`
SessionToken string `xml:"SessionToken"`
Expiration string `xml:"Expiration"`
}
func EncodeFedSessionToken(ownerAk, ownerSk, fedAk, fedSk, name, policy, expireUnix string) (token string, err error) {
encoding, err := NewStsEncoding(fedAk, ownerSk)
if err != nil {
return
}
toEncrypt := strings.Join([]string{
fedAk,
fedSk,
name,
policy,
expireUnix,
}, stsSep)
token = base64.URLEncoding.EncodeToString([]byte(ownerAk + stsSep + encoding.Encrypt([]byte(toEncrypt))))
return
}
type FedDecodeResult struct {
FedSK string
Policy *PolicyV2
UserInfo *proto.UserInfo
}
func DecodeFedSessionToken(fedAk, session string, getUserInfo func(ak string) (*proto.UserInfo, error)) (*FedDecodeResult, error) {
if !strings.HasPrefix(fedAk, stsAkPrefix) {
return nil, InvalidAccessKeyId
}
token, err := base64.URLEncoding.DecodeString(session)
if err != nil {
return nil, InvalidToken
}
tokens := strings.Split(string(token), stsSep)
if len(tokens) != 2 {
return nil, InvalidToken
}
ownerAk, encryption := tokens[0], tokens[1]
userInfo, err := getUserInfo(ownerAk)
if err != nil {
return nil, InvalidToken
}
encoding, err := NewStsEncoding(fedAk, userInfo.SecretKey)
if err != nil {
return nil, InvalidToken
}
decryptInfo, err := encoding.Decrypt(encryption)
if err != nil {
return nil, InvalidToken
}
parts := strings.Split(string(decryptInfo), stsSep)
if len(parts) != 5 {
return nil, InvalidToken
}
fedAk1, fedSk, policyStr, expireUnixStr := parts[0], parts[1], parts[3], parts[4]
if fedAk != fedAk1 {
return nil, InvalidToken
}
expireUnix, err := strconv.ParseInt(expireUnixStr, 10, 64)
if err != nil {
return nil, InvalidToken
}
if time.Now().UTC().Unix() > expireUnix {
return nil, ExpiredToken
}
var policy PolicyV2
if err = json.Unmarshal([]byte(policyStr), &policy); err != nil {
return nil, InvalidToken
}
return &FedDecodeResult{UserInfo: userInfo, FedSK: fedSk, Policy: &policy}, nil
}
func NewStsEncoding(block, key string) (*StsEncoding, error) {
bk, err := aes.NewCipher(MakeSha256([]byte(block)))
if err != nil {
return nil, err
}
return &StsEncoding{
encoding: base64.RawURLEncoding,
block: bk,
key: MakeMD5([]byte(key)),
}, nil
}
type StsEncoding struct {
encoding *base64.Encoding
block cipher.Block
key []byte
}
func (ec *StsEncoding) Encrypt(data []byte) string {
buf := make([]byte, len(data))
cfb := cipher.NewCFBEncrypter(ec.block, ec.key)
cfb.XORKeyStream(buf, data)
return ec.encoding.EncodeToString(buf)
}
func (ec *StsEncoding) Decrypt(s string) ([]byte, error) {
data, err := ec.encoding.DecodeString(s)
if err != nil {
return nil, err
}
buf := make([]byte, len(data))
cfb := cipher.NewCFBDecrypter(ec.block, ec.key)
cfb.XORKeyStream(buf, data)
return buf, nil
}