2020-02-09 22:30:02 +00:00
|
|
|
package s3api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2021-10-14 04:27:58 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"strings"
|
2021-11-09 09:19:50 +00:00
|
|
|
"sync"
|
2021-10-14 04:27:58 +00:00
|
|
|
|
2022-07-29 07:17:28 +00:00
|
|
|
"github.com/seaweedfs/seaweedfs/weed/filer"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
2020-02-09 22:30:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Action string
|
|
|
|
|
|
|
|
type Iam interface {
|
|
|
|
Check(f http.HandlerFunc, actions ...Action) http.HandlerFunc
|
|
|
|
}
|
|
|
|
|
|
|
|
type IdentityAccessManagement struct {
|
2021-11-09 15:11:06 +00:00
|
|
|
m sync.RWMutex
|
2021-11-09 09:19:50 +00:00
|
|
|
|
2023-09-21 15:19:11 +00:00
|
|
|
identities []*Identity
|
|
|
|
accessKeyIdent map[string]*Identity
|
2023-09-25 15:34:12 +00:00
|
|
|
accounts map[string]*Account
|
|
|
|
emailAccount map[string]*Account
|
2023-09-21 15:19:11 +00:00
|
|
|
hashes map[string]*sync.Pool
|
|
|
|
hashCounters map[string]*int32
|
|
|
|
identityAnonymous *Identity
|
|
|
|
hashMu sync.RWMutex
|
|
|
|
domain string
|
|
|
|
isAuthEnabled bool
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Identity struct {
|
|
|
|
Name string
|
2023-09-25 15:34:12 +00:00
|
|
|
Account *Account
|
2020-02-09 22:30:02 +00:00
|
|
|
Credentials []*Credential
|
|
|
|
Actions []Action
|
|
|
|
}
|
|
|
|
|
2023-09-25 15:34:12 +00:00
|
|
|
// Account represents a system user, a system user can
|
|
|
|
// configure multiple IAM-Users, IAM-Users can configure
|
|
|
|
// permissions respectively, and each IAM-User can
|
|
|
|
// configure multiple security credentials
|
|
|
|
type Account struct {
|
|
|
|
//Name is also used to display the "DisplayName" as the owner of the bucket or object
|
|
|
|
DisplayName string
|
|
|
|
EmailAddress string
|
|
|
|
|
|
|
|
//Id is used to identify an Account when granting cross-account access(ACLs) to buckets and objects
|
|
|
|
Id string
|
2022-10-02 02:18:00 +00:00
|
|
|
}
|
|
|
|
|
2023-09-25 15:34:12 +00:00
|
|
|
// Predefined Accounts
|
|
|
|
var (
|
|
|
|
// AccountAdmin is used as the default account for IAM-Credentials access without Account configured
|
|
|
|
AccountAdmin = Account{
|
|
|
|
DisplayName: "admin",
|
|
|
|
EmailAddress: "admin@example.com",
|
|
|
|
Id: s3_constants.AccountAdminId,
|
|
|
|
}
|
|
|
|
|
|
|
|
// AccountAnonymous is used to represent the account for anonymous access
|
|
|
|
AccountAnonymous = Account{
|
|
|
|
DisplayName: "anonymous",
|
|
|
|
EmailAddress: "anonymous@example.com",
|
|
|
|
Id: s3_constants.AccountAnonymousId,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2020-02-09 22:30:02 +00:00
|
|
|
type Credential struct {
|
|
|
|
AccessKey string
|
|
|
|
SecretKey string
|
|
|
|
}
|
|
|
|
|
2023-09-25 15:34:12 +00:00
|
|
|
func (i *Identity) isAnonymous() bool {
|
|
|
|
return i.Account.Id == s3_constants.AccountAnonymousId
|
|
|
|
}
|
|
|
|
|
2021-10-11 10:03:56 +00:00
|
|
|
func (action Action) isAdmin() bool {
|
|
|
|
return strings.HasPrefix(string(action), s3_constants.ACTION_ADMIN)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (action Action) isOwner(bucket string) bool {
|
|
|
|
return string(action) == s3_constants.ACTION_ADMIN+":"+bucket
|
|
|
|
}
|
|
|
|
|
|
|
|
func (action Action) overBucket(bucket string) bool {
|
|
|
|
return strings.HasSuffix(string(action), ":"+bucket) || strings.HasSuffix(string(action), ":*")
|
|
|
|
}
|
|
|
|
|
2023-09-25 15:34:12 +00:00
|
|
|
// "Permission": "FULL_CONTROL"|"WRITE"|"WRITE_ACP"|"READ"|"READ_ACP"
|
2021-10-11 10:03:56 +00:00
|
|
|
func (action Action) getPermission() Permission {
|
|
|
|
switch act := strings.Split(string(action), ":")[0]; act {
|
|
|
|
case s3_constants.ACTION_ADMIN:
|
|
|
|
return Permission("FULL_CONTROL")
|
|
|
|
case s3_constants.ACTION_WRITE:
|
|
|
|
return Permission("WRITE")
|
2023-09-25 15:34:12 +00:00
|
|
|
case s3_constants.ACTION_WRITE_ACP:
|
|
|
|
return Permission("WRITE_ACP")
|
2021-10-11 10:03:56 +00:00
|
|
|
case s3_constants.ACTION_READ:
|
|
|
|
return Permission("READ")
|
2023-09-25 15:34:12 +00:00
|
|
|
case s3_constants.ACTION_READ_ACP:
|
|
|
|
return Permission("READ_ACP")
|
2021-10-11 10:03:56 +00:00
|
|
|
default:
|
|
|
|
return Permission("")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-03 16:45:56 +00:00
|
|
|
func NewIdentityAccessManagement(option *S3ApiServerOption) *IdentityAccessManagement {
|
2020-02-10 00:02:05 +00:00
|
|
|
iam := &IdentityAccessManagement{
|
2023-09-01 17:57:04 +00:00
|
|
|
domain: option.DomainName,
|
|
|
|
hashes: make(map[string]*sync.Pool),
|
|
|
|
hashCounters: make(map[string]*int32),
|
2020-02-10 00:02:05 +00:00
|
|
|
}
|
2020-12-10 08:59:04 +00:00
|
|
|
if option.Config != "" {
|
2020-11-25 20:30:11 +00:00
|
|
|
if err := iam.loadS3ApiConfigurationFromFile(option.Config); err != nil {
|
2020-11-03 16:45:56 +00:00
|
|
|
glog.Fatalf("fail to load config file %s: %v", option.Config, err)
|
|
|
|
}
|
2020-12-10 08:59:04 +00:00
|
|
|
} else {
|
|
|
|
if err := iam.loadS3ApiConfigurationFromFiler(option); err != nil {
|
|
|
|
glog.Warningf("fail to load config: %v", err)
|
|
|
|
}
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
|
|
|
return iam
|
|
|
|
}
|
|
|
|
|
2021-07-09 09:48:03 +00:00
|
|
|
func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFiler(option *S3ApiServerOption) (err error) {
|
|
|
|
var content []byte
|
2023-01-20 09:48:12 +00:00
|
|
|
err = pb.WithFilerClient(false, 0, option.Filer, option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
|
2022-09-14 17:11:31 +00:00
|
|
|
content, err = filer.ReadInsideFiler(client, filer.IamConfigDirectory, filer.IamIdentityFile)
|
2021-07-09 09:48:03 +00:00
|
|
|
return err
|
|
|
|
})
|
2020-12-07 07:16:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("read S3 config: %v", err)
|
|
|
|
}
|
2022-05-15 07:43:37 +00:00
|
|
|
return iam.LoadS3ApiConfigurationFromBytes(content)
|
2020-11-25 20:30:11 +00:00
|
|
|
}
|
2020-02-09 22:30:02 +00:00
|
|
|
|
2020-11-25 20:30:11 +00:00
|
|
|
func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFile(fileName string) error {
|
2021-10-14 04:27:58 +00:00
|
|
|
content, readErr := os.ReadFile(fileName)
|
2020-02-09 22:30:02 +00:00
|
|
|
if readErr != nil {
|
|
|
|
glog.Warningf("fail to read %s : %v", fileName, readErr)
|
|
|
|
return fmt.Errorf("fail to read %s : %v", fileName, readErr)
|
|
|
|
}
|
2022-05-15 07:43:37 +00:00
|
|
|
return iam.LoadS3ApiConfigurationFromBytes(content)
|
2020-12-10 08:15:22 +00:00
|
|
|
}
|
2020-02-09 22:30:02 +00:00
|
|
|
|
2022-05-15 07:43:37 +00:00
|
|
|
func (iam *IdentityAccessManagement) LoadS3ApiConfigurationFromBytes(content []byte) error {
|
2020-12-10 08:15:22 +00:00
|
|
|
s3ApiConfiguration := &iam_pb.S3ApiConfiguration{}
|
|
|
|
if err := filer.ParseS3ConfigurationFromBytes(content, s3ApiConfiguration); err != nil {
|
2020-02-09 22:30:02 +00:00
|
|
|
glog.Warningf("unmarshal error: %v", err)
|
2020-12-10 08:15:22 +00:00
|
|
|
return fmt.Errorf("unmarshal error: %v", err)
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
2022-07-13 09:28:20 +00:00
|
|
|
|
|
|
|
if err := filer.CheckDuplicateAccessKey(s3ApiConfiguration); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-25 20:30:11 +00:00
|
|
|
if err := iam.loadS3ApiConfiguration(s3ApiConfiguration); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2020-02-09 22:30:02 +00:00
|
|
|
|
2020-11-25 20:30:11 +00:00
|
|
|
func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error {
|
2020-12-07 08:10:29 +00:00
|
|
|
var identities []*Identity
|
2023-09-21 15:19:11 +00:00
|
|
|
var identityAnonymous *Identity
|
|
|
|
accessKeyIdent := make(map[string]*Identity)
|
2023-09-25 15:34:12 +00:00
|
|
|
accounts := make(map[string]*Account)
|
|
|
|
emailAccount := make(map[string]*Account)
|
|
|
|
foundAccountAdmin := false
|
|
|
|
foundAccountAnonymous := false
|
|
|
|
|
|
|
|
for _, account := range config.Accounts {
|
|
|
|
switch account.Id {
|
|
|
|
case AccountAdmin.Id:
|
|
|
|
AccountAdmin = Account{
|
|
|
|
Id: account.Id,
|
|
|
|
DisplayName: account.DisplayName,
|
|
|
|
EmailAddress: account.EmailAddress,
|
|
|
|
}
|
|
|
|
accounts[account.Id] = &AccountAdmin
|
|
|
|
foundAccountAdmin = true
|
|
|
|
case AccountAnonymous.Id:
|
|
|
|
AccountAnonymous = Account{
|
|
|
|
Id: account.Id,
|
|
|
|
DisplayName: account.DisplayName,
|
|
|
|
EmailAddress: account.EmailAddress,
|
|
|
|
}
|
|
|
|
accounts[account.Id] = &AccountAnonymous
|
|
|
|
foundAccountAnonymous = true
|
|
|
|
default:
|
|
|
|
t := Account{
|
|
|
|
Id: account.Id,
|
|
|
|
DisplayName: account.DisplayName,
|
|
|
|
EmailAddress: account.EmailAddress,
|
|
|
|
}
|
|
|
|
accounts[account.Id] = &t
|
|
|
|
}
|
|
|
|
if account.EmailAddress != "" {
|
|
|
|
emailAccount[account.EmailAddress] = accounts[account.Id]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !foundAccountAdmin {
|
|
|
|
accounts[AccountAdmin.Id] = &AccountAdmin
|
|
|
|
emailAccount[AccountAdmin.EmailAddress] = &AccountAdmin
|
|
|
|
}
|
|
|
|
if !foundAccountAnonymous {
|
|
|
|
accounts[AccountAnonymous.Id] = &AccountAnonymous
|
|
|
|
emailAccount[AccountAnonymous.EmailAddress] = &AccountAnonymous
|
|
|
|
}
|
2020-11-25 20:30:11 +00:00
|
|
|
for _, ident := range config.Identities {
|
2020-02-09 22:30:02 +00:00
|
|
|
t := &Identity{
|
|
|
|
Name: ident.Name,
|
|
|
|
Credentials: nil,
|
|
|
|
Actions: nil,
|
|
|
|
}
|
2023-09-25 15:34:12 +00:00
|
|
|
switch {
|
|
|
|
case ident.Name == AccountAnonymous.Id:
|
|
|
|
t.Account = &AccountAnonymous
|
2023-09-21 15:19:11 +00:00
|
|
|
identityAnonymous = t
|
2023-09-25 15:34:12 +00:00
|
|
|
case ident.Account == nil:
|
|
|
|
t.Account = &AccountAdmin
|
|
|
|
default:
|
|
|
|
if account, ok := accounts[ident.Account.Id]; ok {
|
|
|
|
t.Account = account
|
|
|
|
} else {
|
|
|
|
t.Account = &AccountAdmin
|
|
|
|
glog.Warningf("identity %s is associated with a non exist account ID, the association is invalid", ident.Name)
|
2022-10-02 02:18:00 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-09 22:30:02 +00:00
|
|
|
for _, action := range ident.Actions {
|
|
|
|
t.Actions = append(t.Actions, Action(action))
|
|
|
|
}
|
|
|
|
for _, cred := range ident.Credentials {
|
|
|
|
t.Credentials = append(t.Credentials, &Credential{
|
|
|
|
AccessKey: cred.AccessKey,
|
|
|
|
SecretKey: cred.SecretKey,
|
|
|
|
})
|
2023-09-21 15:19:11 +00:00
|
|
|
accessKeyIdent[cred.AccessKey] = t
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
2020-12-07 08:10:29 +00:00
|
|
|
identities = append(identities, t)
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
2023-09-25 15:34:12 +00:00
|
|
|
|
2021-11-09 09:19:50 +00:00
|
|
|
iam.m.Lock()
|
2020-12-07 08:10:29 +00:00
|
|
|
// atomically switch
|
|
|
|
iam.identities = identities
|
2023-09-21 15:19:11 +00:00
|
|
|
iam.identityAnonymous = identityAnonymous
|
2023-09-25 15:34:12 +00:00
|
|
|
iam.accounts = accounts
|
|
|
|
iam.emailAccount = emailAccount
|
2023-09-21 15:19:11 +00:00
|
|
|
iam.accessKeyIdent = accessKeyIdent
|
2022-05-30 02:00:03 +00:00
|
|
|
if !iam.isAuthEnabled { // one-directional, no toggling
|
|
|
|
iam.isAuthEnabled = len(identities) > 0
|
|
|
|
}
|
2021-11-09 09:19:50 +00:00
|
|
|
iam.m.Unlock()
|
2023-09-25 15:34:12 +00:00
|
|
|
|
2020-02-09 22:30:02 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-11 16:11:15 +00:00
|
|
|
func (iam *IdentityAccessManagement) isEnabled() bool {
|
2022-05-30 02:00:03 +00:00
|
|
|
return iam.isAuthEnabled
|
2020-07-11 16:11:15 +00:00
|
|
|
}
|
|
|
|
|
2020-02-09 22:30:02 +00:00
|
|
|
func (iam *IdentityAccessManagement) lookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) {
|
2021-11-09 15:11:06 +00:00
|
|
|
iam.m.RLock()
|
|
|
|
defer iam.m.RUnlock()
|
2023-09-21 15:19:11 +00:00
|
|
|
if ident, ok := iam.accessKeyIdent[accessKey]; ok {
|
|
|
|
for _, credential := range ident.Credentials {
|
|
|
|
if credential.AccessKey == accessKey {
|
|
|
|
return ident, credential, true
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-07 20:37:46 +00:00
|
|
|
glog.V(1).Infof("could not find accessKey %s", accessKey)
|
2020-02-09 22:30:02 +00:00
|
|
|
return nil, nil, false
|
|
|
|
}
|
|
|
|
|
2020-08-06 10:41:34 +00:00
|
|
|
func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, found bool) {
|
2021-11-09 15:11:06 +00:00
|
|
|
iam.m.RLock()
|
|
|
|
defer iam.m.RUnlock()
|
2023-09-21 15:19:11 +00:00
|
|
|
if iam.identityAnonymous != nil {
|
|
|
|
return iam.identityAnonymous, true
|
2020-08-06 10:41:34 +00:00
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
2023-09-25 15:34:12 +00:00
|
|
|
func (iam *IdentityAccessManagement) GetAccountNameById(canonicalId string) string {
|
|
|
|
iam.m.RLock()
|
|
|
|
defer iam.m.RUnlock()
|
|
|
|
if account, ok := iam.accounts[canonicalId]; ok {
|
|
|
|
return account.DisplayName
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (iam *IdentityAccessManagement) GetAccountIdByEmail(email string) string {
|
|
|
|
iam.m.RLock()
|
|
|
|
defer iam.m.RUnlock()
|
|
|
|
if account, ok := iam.emailAccount[email]; ok {
|
|
|
|
return account.Id
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2020-02-23 05:34:18 +00:00
|
|
|
func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) http.HandlerFunc {
|
2020-02-09 22:30:02 +00:00
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2022-06-13 05:23:26 +00:00
|
|
|
if !iam.isEnabled() {
|
|
|
|
f(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-11-11 08:20:59 +00:00
|
|
|
identity, errCode := iam.authRequest(r, action)
|
2020-09-19 21:09:58 +00:00
|
|
|
if errCode == s3err.ErrNone {
|
2020-11-11 08:20:59 +00:00
|
|
|
if identity != nil && identity.Name != "" {
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzIdentityId, identity.Name)
|
2020-11-12 21:57:54 +00:00
|
|
|
if identity.isAdmin() {
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzIsAdmin, "true")
|
|
|
|
} else if _, ok := r.Header[s3_constants.AmzIsAdmin]; ok {
|
|
|
|
r.Header.Del(s3_constants.AmzIsAdmin)
|
2020-11-12 21:57:54 +00:00
|
|
|
}
|
2020-11-11 08:20:59 +00:00
|
|
|
}
|
2020-02-09 22:30:02 +00:00
|
|
|
f(w, r)
|
|
|
|
return
|
|
|
|
}
|
2021-11-01 01:05:34 +00:00
|
|
|
s3err.WriteErrorResponse(w, r, errCode)
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check whether the request has valid access keys
|
2020-11-11 08:20:59 +00:00
|
|
|
func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) (*Identity, s3err.ErrorCode) {
|
2020-12-28 05:09:45 +00:00
|
|
|
var identity *Identity
|
|
|
|
var s3Err s3err.ErrorCode
|
|
|
|
var found bool
|
2021-12-10 14:40:32 +00:00
|
|
|
var authType string
|
2020-12-28 05:09:45 +00:00
|
|
|
switch getRequestAuthType(r) {
|
|
|
|
case authTypeStreamingSigned:
|
|
|
|
return identity, s3err.ErrNone
|
|
|
|
case authTypeUnknown:
|
|
|
|
glog.V(3).Infof("unknown auth type")
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, "Unknown")
|
2020-12-28 05:09:45 +00:00
|
|
|
return identity, s3err.ErrAccessDenied
|
|
|
|
case authTypePresignedV2, authTypeSignedV2:
|
|
|
|
glog.V(3).Infof("v2 auth type")
|
|
|
|
identity, s3Err = iam.isReqAuthenticatedV2(r)
|
2021-12-10 14:40:32 +00:00
|
|
|
authType = "SigV2"
|
2020-12-28 05:09:45 +00:00
|
|
|
case authTypeSigned, authTypePresigned:
|
|
|
|
glog.V(3).Infof("v4 auth type")
|
|
|
|
identity, s3Err = iam.reqSignatureV4Verify(r)
|
2021-12-10 14:40:32 +00:00
|
|
|
authType = "SigV4"
|
2020-12-28 05:09:45 +00:00
|
|
|
case authTypePostPolicy:
|
|
|
|
glog.V(3).Infof("post policy auth type")
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, "PostPolicy")
|
2020-12-28 05:09:45 +00:00
|
|
|
return identity, s3err.ErrNone
|
|
|
|
case authTypeJWT:
|
|
|
|
glog.V(3).Infof("jwt auth type")
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, "Jwt")
|
2020-12-28 05:09:45 +00:00
|
|
|
return identity, s3err.ErrNotImplemented
|
|
|
|
case authTypeAnonymous:
|
2021-12-10 14:40:32 +00:00
|
|
|
authType = "Anonymous"
|
2023-09-21 15:19:11 +00:00
|
|
|
if identity, found = iam.lookupAnonymous(); !found {
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, authType)
|
2020-12-28 05:09:45 +00:00
|
|
|
return identity, s3err.ErrAccessDenied
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return identity, s3err.ErrNotImplemented
|
|
|
|
}
|
|
|
|
|
2021-12-10 14:40:32 +00:00
|
|
|
if len(authType) > 0 {
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, authType)
|
2021-12-10 14:40:32 +00:00
|
|
|
}
|
2020-12-25 08:38:56 +00:00
|
|
|
if s3Err != s3err.ErrNone {
|
|
|
|
return identity, s3Err
|
|
|
|
}
|
|
|
|
|
2021-07-03 21:51:01 +00:00
|
|
|
glog.V(3).Infof("user name: %v actions: %v, action: %v", identity.Name, identity.Actions, action)
|
2020-12-25 08:38:56 +00:00
|
|
|
|
2022-05-31 05:57:41 +00:00
|
|
|
bucket, object := s3_constants.GetBucketAndObject(r)
|
2020-12-25 08:38:56 +00:00
|
|
|
|
2022-01-03 23:39:36 +00:00
|
|
|
if !identity.canDo(action, bucket, object) {
|
2020-12-25 08:38:56 +00:00
|
|
|
return identity, s3err.ErrAccessDenied
|
|
|
|
}
|
|
|
|
|
2023-09-25 15:34:12 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAccountId, identity.Account.Id)
|
|
|
|
|
2020-12-25 08:38:56 +00:00
|
|
|
return identity, s3err.ErrNone
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (iam *IdentityAccessManagement) authUser(r *http.Request) (*Identity, s3err.ErrorCode) {
|
2020-02-09 22:30:02 +00:00
|
|
|
var identity *Identity
|
2020-09-19 21:09:58 +00:00
|
|
|
var s3Err s3err.ErrorCode
|
2020-08-06 10:41:34 +00:00
|
|
|
var found bool
|
2021-12-10 14:40:32 +00:00
|
|
|
var authType string
|
2020-02-09 22:30:02 +00:00
|
|
|
switch getRequestAuthType(r) {
|
2020-02-10 00:02:05 +00:00
|
|
|
case authTypeStreamingSigned:
|
2020-11-11 08:20:59 +00:00
|
|
|
return identity, s3err.ErrNone
|
2020-02-10 00:02:05 +00:00
|
|
|
case authTypeUnknown:
|
|
|
|
glog.V(3).Infof("unknown auth type")
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, "Unknown")
|
2020-11-11 08:20:59 +00:00
|
|
|
return identity, s3err.ErrAccessDenied
|
2020-02-09 22:30:02 +00:00
|
|
|
case authTypePresignedV2, authTypeSignedV2:
|
2020-02-10 00:02:05 +00:00
|
|
|
glog.V(3).Infof("v2 auth type")
|
|
|
|
identity, s3Err = iam.isReqAuthenticatedV2(r)
|
2021-12-10 14:40:32 +00:00
|
|
|
authType = "SigV2"
|
2020-02-09 22:30:02 +00:00
|
|
|
case authTypeSigned, authTypePresigned:
|
2020-02-10 00:02:05 +00:00
|
|
|
glog.V(3).Infof("v4 auth type")
|
2020-02-09 22:30:02 +00:00
|
|
|
identity, s3Err = iam.reqSignatureV4Verify(r)
|
2021-12-10 14:40:32 +00:00
|
|
|
authType = "SigV4"
|
add unhandled request auth type
fix
2020-02-18 11:43:57.396699 I | http: panic serving 172.28.0.43:50658: runtime error: invalid memory address or nil pointer dereference
goroutine 595 [running]:
net/http.(*conn).serve.func1(0xc0001fe3c0)
/usr/lib/go/src/net/http/server.go:1767 +0x13b
panic(0x55c4e35f3820, 0x55c4e48b3c40)
/usr/lib/go/src/runtime/panic.go:679 +0x1b6
github.com/chrislusf/seaweedfs/weed/s3api.(*IdentityAccessManagement).authRequest(0xc0004b84e0, 0xc000115900, 0xc0000bb650, 0x1, 0x1, 0x55c4e399d740)
/go/src/github.com/chrislusf/seaweedfs/weed/s3api/auth_credentials.go:143 +0x11c
github.com/chrislusf/seaweedfs/weed/s3api.(*IdentityAccessManagement).Auth.func1(0x55c4e3994c40, 0xc0007808c0, 0xc000115900)
/go/src/github.com/chrislusf/seaweedfs/weed/s3api/auth_credentials.go:111 +0x5e
net/http.HandlerFunc.ServeHTTP(0xc0004b87e0, 0x55c4e3994c40, 0xc0007808c0, 0xc000115900)
/usr/lib/go/src/net/http/server.go:2007 +0x46
github.com/gorilla/mux.(*Router).ServeHTTP(0xc0004ba000, 0x55c4e3994c40, 0xc0007808c0, 0xc000115700)
/root/go/pkg/mod/github.com/gorilla/mux@v1.7.3/mux.go:212 +0xe4
net/http.serverHandler.ServeHTTP(0xc00011e0e0, 0x55c4e3994c40, 0xc0007808c0, 0xc000115700)
/usr/lib/go/src/net/http/server.go:2802 +0xa6
net/http.(*conn).serve(0xc0001fe3c0, 0x55c4e399d680, 0xc000894180)
/usr/lib/go/src/net/http/server.go:1890 +0x877
created by net/http.(*Server).Serve
/usr/lib/go/src/net/http/server.go:2927 +0x390
2020-02-18 17:16:04 +00:00
|
|
|
case authTypePostPolicy:
|
2020-02-22 22:01:04 +00:00
|
|
|
glog.V(3).Infof("post policy auth type")
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, "PostPolicy")
|
2020-11-11 08:20:59 +00:00
|
|
|
return identity, s3err.ErrNone
|
add unhandled request auth type
fix
2020-02-18 11:43:57.396699 I | http: panic serving 172.28.0.43:50658: runtime error: invalid memory address or nil pointer dereference
goroutine 595 [running]:
net/http.(*conn).serve.func1(0xc0001fe3c0)
/usr/lib/go/src/net/http/server.go:1767 +0x13b
panic(0x55c4e35f3820, 0x55c4e48b3c40)
/usr/lib/go/src/runtime/panic.go:679 +0x1b6
github.com/chrislusf/seaweedfs/weed/s3api.(*IdentityAccessManagement).authRequest(0xc0004b84e0, 0xc000115900, 0xc0000bb650, 0x1, 0x1, 0x55c4e399d740)
/go/src/github.com/chrislusf/seaweedfs/weed/s3api/auth_credentials.go:143 +0x11c
github.com/chrislusf/seaweedfs/weed/s3api.(*IdentityAccessManagement).Auth.func1(0x55c4e3994c40, 0xc0007808c0, 0xc000115900)
/go/src/github.com/chrislusf/seaweedfs/weed/s3api/auth_credentials.go:111 +0x5e
net/http.HandlerFunc.ServeHTTP(0xc0004b87e0, 0x55c4e3994c40, 0xc0007808c0, 0xc000115900)
/usr/lib/go/src/net/http/server.go:2007 +0x46
github.com/gorilla/mux.(*Router).ServeHTTP(0xc0004ba000, 0x55c4e3994c40, 0xc0007808c0, 0xc000115700)
/root/go/pkg/mod/github.com/gorilla/mux@v1.7.3/mux.go:212 +0xe4
net/http.serverHandler.ServeHTTP(0xc00011e0e0, 0x55c4e3994c40, 0xc0007808c0, 0xc000115700)
/usr/lib/go/src/net/http/server.go:2802 +0xa6
net/http.(*conn).serve(0xc0001fe3c0, 0x55c4e399d680, 0xc000894180)
/usr/lib/go/src/net/http/server.go:1890 +0x877
created by net/http.(*Server).Serve
/usr/lib/go/src/net/http/server.go:2927 +0x390
2020-02-18 17:16:04 +00:00
|
|
|
case authTypeJWT:
|
2020-02-22 22:01:04 +00:00
|
|
|
glog.V(3).Infof("jwt auth type")
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, "Jwt")
|
2020-11-11 08:20:59 +00:00
|
|
|
return identity, s3err.ErrNotImplemented
|
add unhandled request auth type
fix
2020-02-18 11:43:57.396699 I | http: panic serving 172.28.0.43:50658: runtime error: invalid memory address or nil pointer dereference
goroutine 595 [running]:
net/http.(*conn).serve.func1(0xc0001fe3c0)
/usr/lib/go/src/net/http/server.go:1767 +0x13b
panic(0x55c4e35f3820, 0x55c4e48b3c40)
/usr/lib/go/src/runtime/panic.go:679 +0x1b6
github.com/chrislusf/seaweedfs/weed/s3api.(*IdentityAccessManagement).authRequest(0xc0004b84e0, 0xc000115900, 0xc0000bb650, 0x1, 0x1, 0x55c4e399d740)
/go/src/github.com/chrislusf/seaweedfs/weed/s3api/auth_credentials.go:143 +0x11c
github.com/chrislusf/seaweedfs/weed/s3api.(*IdentityAccessManagement).Auth.func1(0x55c4e3994c40, 0xc0007808c0, 0xc000115900)
/go/src/github.com/chrislusf/seaweedfs/weed/s3api/auth_credentials.go:111 +0x5e
net/http.HandlerFunc.ServeHTTP(0xc0004b87e0, 0x55c4e3994c40, 0xc0007808c0, 0xc000115900)
/usr/lib/go/src/net/http/server.go:2007 +0x46
github.com/gorilla/mux.(*Router).ServeHTTP(0xc0004ba000, 0x55c4e3994c40, 0xc0007808c0, 0xc000115700)
/root/go/pkg/mod/github.com/gorilla/mux@v1.7.3/mux.go:212 +0xe4
net/http.serverHandler.ServeHTTP(0xc00011e0e0, 0x55c4e3994c40, 0xc0007808c0, 0xc000115700)
/usr/lib/go/src/net/http/server.go:2802 +0xa6
net/http.(*conn).serve(0xc0001fe3c0, 0x55c4e399d680, 0xc000894180)
/usr/lib/go/src/net/http/server.go:1890 +0x877
created by net/http.(*Server).Serve
/usr/lib/go/src/net/http/server.go:2927 +0x390
2020-02-18 17:16:04 +00:00
|
|
|
case authTypeAnonymous:
|
2021-12-10 14:40:32 +00:00
|
|
|
authType = "Anonymous"
|
2020-08-06 10:41:34 +00:00
|
|
|
identity, found = iam.lookupAnonymous()
|
|
|
|
if !found {
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, authType)
|
2020-11-11 08:20:59 +00:00
|
|
|
return identity, s3err.ErrAccessDenied
|
2020-08-06 10:41:34 +00:00
|
|
|
}
|
2020-02-22 22:01:04 +00:00
|
|
|
default:
|
2020-11-11 08:20:59 +00:00
|
|
|
return identity, s3err.ErrNotImplemented
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
2021-12-10 14:40:32 +00:00
|
|
|
|
|
|
|
if len(authType) > 0 {
|
2022-05-31 05:57:41 +00:00
|
|
|
r.Header.Set(s3_constants.AmzAuthType, authType)
|
2021-12-10 14:40:32 +00:00
|
|
|
}
|
2020-02-09 22:30:02 +00:00
|
|
|
|
2020-02-10 00:02:05 +00:00
|
|
|
glog.V(3).Infof("auth error: %v", s3Err)
|
2020-09-19 21:09:58 +00:00
|
|
|
if s3Err != s3err.ErrNone {
|
2020-11-11 08:20:59 +00:00
|
|
|
return identity, s3Err
|
2020-02-10 00:02:05 +00:00
|
|
|
}
|
2020-11-11 08:20:59 +00:00
|
|
|
return identity, s3err.ErrNone
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-03 23:39:36 +00:00
|
|
|
func (identity *Identity) canDo(action Action, bucket string, objectKey string) bool {
|
2020-11-12 21:57:54 +00:00
|
|
|
if identity.isAdmin() {
|
|
|
|
return true
|
2020-02-23 05:34:18 +00:00
|
|
|
}
|
|
|
|
for _, a := range identity.Actions {
|
|
|
|
if a == action {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if bucket == "" {
|
|
|
|
return false
|
|
|
|
}
|
2022-01-04 05:05:20 +00:00
|
|
|
target := string(action) + ":" + bucket + objectKey
|
2022-01-12 11:04:59 +00:00
|
|
|
adminTarget := s3_constants.ACTION_ADMIN + ":" + bucket + objectKey
|
2020-02-23 05:34:18 +00:00
|
|
|
limitedByBucket := string(action) + ":" + bucket
|
2020-12-25 08:38:56 +00:00
|
|
|
adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket
|
2020-02-23 05:34:18 +00:00
|
|
|
for _, a := range identity.Actions {
|
2021-04-27 16:45:40 +00:00
|
|
|
act := string(a)
|
|
|
|
if strings.HasSuffix(act, "*") {
|
2022-01-03 23:39:36 +00:00
|
|
|
if strings.HasPrefix(target, act[:len(act)-1]) {
|
2021-04-27 16:45:40 +00:00
|
|
|
return true
|
|
|
|
}
|
2022-01-12 11:04:59 +00:00
|
|
|
if strings.HasPrefix(adminTarget, act[:len(act)-1]) {
|
|
|
|
return true
|
|
|
|
}
|
2021-04-27 16:45:40 +00:00
|
|
|
} else {
|
|
|
|
if act == limitedByBucket {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if act == adminLimitedByBucket {
|
|
|
|
return true
|
|
|
|
}
|
2020-12-25 08:38:56 +00:00
|
|
|
}
|
2020-02-09 22:30:02 +00:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2020-11-12 21:57:54 +00:00
|
|
|
|
|
|
|
func (identity *Identity) isAdmin() bool {
|
|
|
|
for _, a := range identity.Actions {
|
|
|
|
if a == "Admin" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|