You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
7.3 KiB

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