auth_credentials.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. package s3api
  2. import (
  3. "fmt"
  4. "github.com/chrislusf/seaweedfs/weed/filer"
  5. "github.com/chrislusf/seaweedfs/weed/glog"
  6. "github.com/chrislusf/seaweedfs/weed/pb"
  7. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  8. "github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
  9. xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
  10. "github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
  11. "github.com/chrislusf/seaweedfs/weed/s3api/s3err"
  12. "io/ioutil"
  13. "net/http"
  14. "strings"
  15. )
  16. type Action string
  17. type Iam interface {
  18. Check(f http.HandlerFunc, actions ...Action) http.HandlerFunc
  19. }
  20. type IdentityAccessManagement struct {
  21. identities []*Identity
  22. domain string
  23. }
  24. type Identity struct {
  25. Name string
  26. Credentials []*Credential
  27. Actions []Action
  28. }
  29. type Credential struct {
  30. AccessKey string
  31. SecretKey string
  32. }
  33. func NewIdentityAccessManagement(option *S3ApiServerOption) *IdentityAccessManagement {
  34. iam := &IdentityAccessManagement{
  35. domain: option.DomainName,
  36. }
  37. if option.Config != "" {
  38. if err := iam.loadS3ApiConfigurationFromFile(option.Config); err != nil {
  39. glog.Fatalf("fail to load config file %s: %v", option.Config, err)
  40. }
  41. } else {
  42. if err := iam.loadS3ApiConfigurationFromFiler(option); err != nil {
  43. glog.Warningf("fail to load config: %v", err)
  44. }
  45. }
  46. return iam
  47. }
  48. func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFiler(option *S3ApiServerOption) (err error) {
  49. var content []byte
  50. err = pb.WithFilerClient(option.Filer, option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  51. content, err = filer.ReadInsideFiler(client, filer.IamConfigDirecotry, filer.IamIdentityFile)
  52. return err
  53. })
  54. if err != nil {
  55. return fmt.Errorf("read S3 config: %v", err)
  56. }
  57. return iam.loadS3ApiConfigurationFromBytes(content)
  58. }
  59. func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFile(fileName string) error {
  60. content, readErr := ioutil.ReadFile(fileName)
  61. if readErr != nil {
  62. glog.Warningf("fail to read %s : %v", fileName, readErr)
  63. return fmt.Errorf("fail to read %s : %v", fileName, readErr)
  64. }
  65. return iam.loadS3ApiConfigurationFromBytes(content)
  66. }
  67. func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromBytes(content []byte) error {
  68. s3ApiConfiguration := &iam_pb.S3ApiConfiguration{}
  69. if err := filer.ParseS3ConfigurationFromBytes(content, s3ApiConfiguration); err != nil {
  70. glog.Warningf("unmarshal error: %v", err)
  71. return fmt.Errorf("unmarshal error: %v", err)
  72. }
  73. if err := iam.loadS3ApiConfiguration(s3ApiConfiguration); err != nil {
  74. return err
  75. }
  76. return nil
  77. }
  78. func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error {
  79. var identities []*Identity
  80. for _, ident := range config.Identities {
  81. t := &Identity{
  82. Name: ident.Name,
  83. Credentials: nil,
  84. Actions: nil,
  85. }
  86. for _, action := range ident.Actions {
  87. t.Actions = append(t.Actions, Action(action))
  88. }
  89. for _, cred := range ident.Credentials {
  90. t.Credentials = append(t.Credentials, &Credential{
  91. AccessKey: cred.AccessKey,
  92. SecretKey: cred.SecretKey,
  93. })
  94. }
  95. identities = append(identities, t)
  96. }
  97. // atomically switch
  98. iam.identities = identities
  99. return nil
  100. }
  101. func (iam *IdentityAccessManagement) isEnabled() bool {
  102. return len(iam.identities) > 0
  103. }
  104. func (iam *IdentityAccessManagement) lookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) {
  105. for _, ident := range iam.identities {
  106. for _, cred := range ident.Credentials {
  107. if cred.AccessKey == accessKey {
  108. return ident, cred, true
  109. }
  110. }
  111. }
  112. return nil, nil, false
  113. }
  114. func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, found bool) {
  115. for _, ident := range iam.identities {
  116. if ident.Name == "anonymous" {
  117. return ident, true
  118. }
  119. }
  120. return nil, false
  121. }
  122. func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) http.HandlerFunc {
  123. if !iam.isEnabled() {
  124. return f
  125. }
  126. return func(w http.ResponseWriter, r *http.Request) {
  127. identity, errCode := iam.authRequest(r, action)
  128. if errCode == s3err.ErrNone {
  129. if identity != nil && identity.Name != "" {
  130. r.Header.Set(xhttp.AmzIdentityId, identity.Name)
  131. if identity.isAdmin() {
  132. r.Header.Set(xhttp.AmzIsAdmin, "true")
  133. }
  134. }
  135. f(w, r)
  136. return
  137. }
  138. s3err.WriteErrorResponse(w, errCode, r)
  139. }
  140. }
  141. // check whether the request has valid access keys
  142. func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) (*Identity, s3err.ErrorCode) {
  143. var identity *Identity
  144. var s3Err s3err.ErrorCode
  145. var found bool
  146. switch getRequestAuthType(r) {
  147. case authTypeStreamingSigned:
  148. return identity, s3err.ErrNone
  149. case authTypeUnknown:
  150. glog.V(3).Infof("unknown auth type")
  151. return identity, s3err.ErrAccessDenied
  152. case authTypePresignedV2, authTypeSignedV2:
  153. glog.V(3).Infof("v2 auth type")
  154. identity, s3Err = iam.isReqAuthenticatedV2(r)
  155. case authTypeSigned, authTypePresigned:
  156. glog.V(3).Infof("v4 auth type")
  157. identity, s3Err = iam.reqSignatureV4Verify(r)
  158. case authTypePostPolicy:
  159. glog.V(3).Infof("post policy auth type")
  160. return identity, s3err.ErrNone
  161. case authTypeJWT:
  162. glog.V(3).Infof("jwt auth type")
  163. return identity, s3err.ErrNotImplemented
  164. case authTypeAnonymous:
  165. identity, found = iam.lookupAnonymous()
  166. if !found {
  167. return identity, s3err.ErrAccessDenied
  168. }
  169. default:
  170. return identity, s3err.ErrNotImplemented
  171. }
  172. if s3Err != s3err.ErrNone {
  173. return identity, s3Err
  174. }
  175. glog.V(3).Infof("user name: %v actions: %v, action: %v", identity.Name, identity.Actions, action)
  176. bucket, _ := getBucketAndObject(r)
  177. if !identity.canDo(action, bucket) {
  178. return identity, s3err.ErrAccessDenied
  179. }
  180. return identity, s3err.ErrNone
  181. }
  182. func (iam *IdentityAccessManagement) authUser(r *http.Request) (*Identity, s3err.ErrorCode) {
  183. var identity *Identity
  184. var s3Err s3err.ErrorCode
  185. var found bool
  186. switch getRequestAuthType(r) {
  187. case authTypeStreamingSigned:
  188. return identity, s3err.ErrNone
  189. case authTypeUnknown:
  190. glog.V(3).Infof("unknown auth type")
  191. return identity, s3err.ErrAccessDenied
  192. case authTypePresignedV2, authTypeSignedV2:
  193. glog.V(3).Infof("v2 auth type")
  194. identity, s3Err = iam.isReqAuthenticatedV2(r)
  195. case authTypeSigned, authTypePresigned:
  196. glog.V(3).Infof("v4 auth type")
  197. identity, s3Err = iam.reqSignatureV4Verify(r)
  198. case authTypePostPolicy:
  199. glog.V(3).Infof("post policy auth type")
  200. return identity, s3err.ErrNone
  201. case authTypeJWT:
  202. glog.V(3).Infof("jwt auth type")
  203. return identity, s3err.ErrNotImplemented
  204. case authTypeAnonymous:
  205. identity, found = iam.lookupAnonymous()
  206. if !found {
  207. return identity, s3err.ErrAccessDenied
  208. }
  209. default:
  210. return identity, s3err.ErrNotImplemented
  211. }
  212. glog.V(3).Infof("auth error: %v", s3Err)
  213. if s3Err != s3err.ErrNone {
  214. return identity, s3Err
  215. }
  216. return identity, s3err.ErrNone
  217. }
  218. func (identity *Identity) canDo(action Action, bucket string) bool {
  219. if identity.isAdmin() {
  220. return true
  221. }
  222. for _, a := range identity.Actions {
  223. if a == action {
  224. return true
  225. }
  226. }
  227. if bucket == "" {
  228. return false
  229. }
  230. limitedByBucket := string(action) + ":" + bucket
  231. adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket
  232. for _, a := range identity.Actions {
  233. act := string(a)
  234. if strings.HasSuffix(act, "*") {
  235. if strings.HasPrefix(limitedByBucket, act[:len(act)-1]) {
  236. return true
  237. }
  238. if strings.HasPrefix(adminLimitedByBucket, act[:len(act)-1]) {
  239. return true
  240. }
  241. } else {
  242. if act == limitedByBucket {
  243. return true
  244. }
  245. if act == adminLimitedByBucket {
  246. return true
  247. }
  248. }
  249. }
  250. return false
  251. }
  252. func (identity *Identity) isAdmin() bool {
  253. for _, a := range identity.Actions {
  254. if a == "Admin" {
  255. return true
  256. }
  257. }
  258. return false
  259. }