mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2024-01-19 02:48:24 +00:00
![Chris Lu](/assets/img/avatar_default.png)
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
169 lines
3.8 KiB
Go
169 lines
3.8 KiB
Go
package s3api
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
|
|
"github.com/golang/protobuf/jsonpb"
|
|
|
|
"github.com/chrislusf/seaweedfs/weed/glog"
|
|
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
|
|
)
|
|
|
|
type Action string
|
|
|
|
const (
|
|
ACTION_READ = "Read"
|
|
ACTION_WRITE = "Write"
|
|
ACTION_ADMIN = "Admin"
|
|
)
|
|
|
|
type Iam interface {
|
|
Check(f http.HandlerFunc, actions ...Action) http.HandlerFunc
|
|
}
|
|
|
|
type IdentityAccessManagement struct {
|
|
identities []*Identity
|
|
domain string
|
|
}
|
|
|
|
type Identity struct {
|
|
Name string
|
|
Credentials []*Credential
|
|
Actions []Action
|
|
}
|
|
|
|
type Credential struct {
|
|
AccessKey string
|
|
SecretKey string
|
|
}
|
|
|
|
func NewIdentityAccessManagement(fileName string, domain string) *IdentityAccessManagement {
|
|
iam := &IdentityAccessManagement{
|
|
domain: domain,
|
|
}
|
|
if fileName == "" {
|
|
return iam
|
|
}
|
|
if err := iam.loadS3ApiConfiguration(fileName); err != nil {
|
|
glog.Fatalf("fail to load config file %s: %v", fileName, err)
|
|
}
|
|
return iam
|
|
}
|
|
|
|
func (iam *IdentityAccessManagement) loadS3ApiConfiguration(fileName string) error {
|
|
|
|
s3ApiConfiguration := &iam_pb.S3ApiConfiguration{}
|
|
|
|
rawData, readErr := ioutil.ReadFile(fileName)
|
|
if readErr != nil {
|
|
glog.Warningf("fail to read %s : %v", fileName, readErr)
|
|
return fmt.Errorf("fail to read %s : %v", fileName, readErr)
|
|
}
|
|
|
|
glog.V(1).Infof("maybeLoadVolumeInfo Unmarshal volume info %v", fileName)
|
|
if err := jsonpb.Unmarshal(bytes.NewReader(rawData), s3ApiConfiguration); err != nil {
|
|
glog.Warningf("unmarshal error: %v", err)
|
|
return fmt.Errorf("unmarshal %s error: %v", fileName, err)
|
|
}
|
|
|
|
for _, ident := range s3ApiConfiguration.Identities {
|
|
t := &Identity{
|
|
Name: ident.Name,
|
|
Credentials: nil,
|
|
Actions: nil,
|
|
}
|
|
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,
|
|
})
|
|
}
|
|
iam.identities = append(iam.identities, t)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (iam *IdentityAccessManagement) lookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) {
|
|
for _, ident := range iam.identities {
|
|
for _, cred := range ident.Credentials {
|
|
if cred.AccessKey == accessKey {
|
|
return ident, cred, true
|
|
}
|
|
}
|
|
}
|
|
return nil, nil, false
|
|
}
|
|
|
|
func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, actions ...Action) http.HandlerFunc {
|
|
|
|
if len(iam.identities) == 0 {
|
|
return f
|
|
}
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
errCode := iam.authRequest(r, actions)
|
|
if errCode == ErrNone {
|
|
f(w, r)
|
|
return
|
|
}
|
|
writeErrorResponse(w, errCode, r.URL)
|
|
}
|
|
}
|
|
|
|
// check whether the request has valid access keys
|
|
func (iam *IdentityAccessManagement) authRequest(r *http.Request, actions []Action) ErrorCode {
|
|
var identity *Identity
|
|
var s3Err ErrorCode
|
|
switch getRequestAuthType(r) {
|
|
case authTypeStreamingSigned:
|
|
return ErrNone
|
|
case authTypeUnknown:
|
|
glog.V(3).Infof("unknown auth type")
|
|
return ErrAccessDenied
|
|
case authTypePresignedV2, authTypeSignedV2:
|
|
glog.V(3).Infof("v2 auth type")
|
|
identity, s3Err = iam.isReqAuthenticatedV2(r)
|
|
case authTypeSigned, authTypePresigned:
|
|
glog.V(3).Infof("v4 auth type")
|
|
identity, s3Err = iam.reqSignatureV4Verify(r)
|
|
case authTypePostPolicy:
|
|
return ErrNotImplemented;
|
|
case authTypeJWT:
|
|
return ErrNotImplemented;
|
|
case authTypeAnonymous:
|
|
return ErrNotImplemented
|
|
}
|
|
|
|
glog.V(3).Infof("auth error: %v", s3Err)
|
|
if s3Err != ErrNone {
|
|
return s3Err
|
|
}
|
|
|
|
glog.V(3).Infof("user name: %v actions: %v", identity.Name, identity.Actions)
|
|
|
|
if !identity.canDo(actions) {
|
|
return ErrAccessDenied
|
|
}
|
|
|
|
return ErrNone
|
|
|
|
}
|
|
|
|
func (identity *Identity) canDo(actions []Action) bool {
|
|
for _, a := range identity.Actions {
|
|
for _, b := range actions {
|
|
if a == b {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|