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.

417 lines
12 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
3 years ago
5 years ago
5 years ago
4 years ago
5 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
5 years ago
3 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
3 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
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/seaweedfs/seaweedfs/weed/s3api/s3account"
  5. "net/http"
  6. "os"
  7. "strings"
  8. "sync"
  9. "github.com/seaweedfs/seaweedfs/weed/filer"
  10. "github.com/seaweedfs/seaweedfs/weed/glog"
  11. "github.com/seaweedfs/seaweedfs/weed/pb"
  12. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  13. "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
  14. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  15. "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
  16. )
  17. var IdentityAnonymous *Identity
  18. type Action string
  19. type Iam interface {
  20. Check(f http.HandlerFunc, actions ...Action) http.HandlerFunc
  21. }
  22. type IdentityAccessManagement struct {
  23. m sync.RWMutex
  24. identities []*Identity
  25. isAuthEnabled bool
  26. domain string
  27. }
  28. type Identity struct {
  29. Name string
  30. AccountId string
  31. Credentials []*Credential
  32. Actions []Action
  33. }
  34. func (i *Identity) isAnonymous() bool {
  35. return i.Name == s3account.AccountAnonymous.Name
  36. }
  37. type Credential struct {
  38. AccessKey string
  39. SecretKey string
  40. }
  41. func (action Action) isAdmin() bool {
  42. return strings.HasPrefix(string(action), s3_constants.ACTION_ADMIN)
  43. }
  44. func (action Action) isOwner(bucket string) bool {
  45. return string(action) == s3_constants.ACTION_ADMIN+":"+bucket
  46. }
  47. func (action Action) overBucket(bucket string) bool {
  48. return strings.HasSuffix(string(action), ":"+bucket) || strings.HasSuffix(string(action), ":*")
  49. }
  50. func (action Action) getPermission() Permission {
  51. switch act := strings.Split(string(action), ":")[0]; act {
  52. case s3_constants.ACTION_ADMIN:
  53. return Permission("FULL_CONTROL")
  54. case s3_constants.ACTION_WRITE:
  55. return Permission("WRITE")
  56. case s3_constants.ACTION_READ:
  57. return Permission("READ")
  58. default:
  59. return Permission("")
  60. }
  61. }
  62. func NewIdentityAccessManagement(option *S3ApiServerOption) *IdentityAccessManagement {
  63. iam := &IdentityAccessManagement{
  64. domain: option.DomainName,
  65. }
  66. if option.Config != "" {
  67. if err := iam.loadS3ApiConfigurationFromFile(option.Config); err != nil {
  68. glog.Fatalf("fail to load config file %s: %v", option.Config, err)
  69. }
  70. } else {
  71. if err := iam.loadS3ApiConfigurationFromFiler(option); err != nil {
  72. glog.Warningf("fail to load config: %v", err)
  73. }
  74. }
  75. return iam
  76. }
  77. func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFiler(option *S3ApiServerOption) (err error) {
  78. var content []byte
  79. err = pb.WithFilerClient(false, option.Filer, option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  80. content, err = filer.ReadInsideFiler(client, filer.IamConfigDirectory, filer.IamIdentityFile)
  81. return err
  82. })
  83. if err != nil {
  84. return fmt.Errorf("read S3 config: %v", err)
  85. }
  86. return iam.LoadS3ApiConfigurationFromBytes(content)
  87. }
  88. func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFile(fileName string) error {
  89. content, readErr := os.ReadFile(fileName)
  90. if readErr != nil {
  91. glog.Warningf("fail to read %s : %v", fileName, readErr)
  92. return fmt.Errorf("fail to read %s : %v", fileName, readErr)
  93. }
  94. return iam.LoadS3ApiConfigurationFromBytes(content)
  95. }
  96. func (iam *IdentityAccessManagement) LoadS3ApiConfigurationFromBytes(content []byte) error {
  97. s3ApiConfiguration := &iam_pb.S3ApiConfiguration{}
  98. if err := filer.ParseS3ConfigurationFromBytes(content, s3ApiConfiguration); err != nil {
  99. glog.Warningf("unmarshal error: %v", err)
  100. return fmt.Errorf("unmarshal error: %v", err)
  101. }
  102. if err := filer.CheckDuplicateAccessKey(s3ApiConfiguration); err != nil {
  103. return err
  104. }
  105. if err := iam.loadS3ApiConfiguration(s3ApiConfiguration); err != nil {
  106. return err
  107. }
  108. return nil
  109. }
  110. func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error {
  111. var identities []*Identity
  112. for _, ident := range config.Identities {
  113. t := &Identity{
  114. Name: ident.Name,
  115. AccountId: s3account.AccountAdmin.Id,
  116. Credentials: nil,
  117. Actions: nil,
  118. }
  119. if ident.Name == s3account.AccountAnonymous.Name {
  120. if ident.AccountId != "" && ident.AccountId != s3account.AccountAnonymous.Id {
  121. glog.Warningf("anonymous identity is associated with a non-anonymous account ID, the association is invalid")
  122. }
  123. t.AccountId = s3account.AccountAnonymous.Id
  124. IdentityAnonymous = t
  125. } else {
  126. if len(ident.AccountId) > 0 {
  127. t.AccountId = ident.AccountId
  128. }
  129. }
  130. for _, action := range ident.Actions {
  131. t.Actions = append(t.Actions, Action(action))
  132. }
  133. for _, cred := range ident.Credentials {
  134. t.Credentials = append(t.Credentials, &Credential{
  135. AccessKey: cred.AccessKey,
  136. SecretKey: cred.SecretKey,
  137. })
  138. }
  139. identities = append(identities, t)
  140. }
  141. if IdentityAnonymous == nil {
  142. IdentityAnonymous = &Identity{
  143. Name: s3account.AccountAnonymous.Name,
  144. AccountId: s3account.AccountAnonymous.Id,
  145. }
  146. }
  147. iam.m.Lock()
  148. // atomically switch
  149. iam.identities = identities
  150. if !iam.isAuthEnabled { // one-directional, no toggling
  151. iam.isAuthEnabled = len(identities) > 0
  152. }
  153. iam.m.Unlock()
  154. return nil
  155. }
  156. func (iam *IdentityAccessManagement) isEnabled() bool {
  157. return iam.isAuthEnabled
  158. }
  159. func (iam *IdentityAccessManagement) lookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) {
  160. iam.m.RLock()
  161. defer iam.m.RUnlock()
  162. for _, ident := range iam.identities {
  163. for _, cred := range ident.Credentials {
  164. // println("checking", ident.Name, cred.AccessKey)
  165. if cred.AccessKey == accessKey {
  166. return ident, cred, true
  167. }
  168. }
  169. }
  170. glog.V(1).Infof("could not find accessKey %s", accessKey)
  171. return nil, nil, false
  172. }
  173. func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, found bool) {
  174. iam.m.RLock()
  175. defer iam.m.RUnlock()
  176. for _, ident := range iam.identities {
  177. if ident.isAnonymous() {
  178. return ident, true
  179. }
  180. }
  181. return nil, false
  182. }
  183. func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) http.HandlerFunc {
  184. return Auth(iam, nil, f, action, false)
  185. }
  186. func (s3a *S3ApiServer) Auth(f http.HandlerFunc, action Action, supportAcl bool) http.HandlerFunc {
  187. return Auth(s3a.iam, s3a.bucketRegistry, f, action, supportAcl)
  188. }
  189. func Auth(iam *IdentityAccessManagement, br *BucketRegistry, f http.HandlerFunc, action Action, supportAcl bool) http.HandlerFunc {
  190. return func(w http.ResponseWriter, r *http.Request) {
  191. //unset predefined headers
  192. delete(r.Header, s3_constants.AmzAccountId)
  193. delete(r.Header, s3_constants.ExtAmzOwnerKey)
  194. delete(r.Header, s3_constants.ExtAmzAclKey)
  195. if !iam.isEnabled() {
  196. f(w, r)
  197. return
  198. }
  199. identity, errCode := authRequest(iam, br, r, action, supportAcl)
  200. if errCode == s3err.ErrNone {
  201. if identity != nil && identity.Name != "" {
  202. r.Header.Set(s3_constants.AmzIdentityId, identity.Name)
  203. if identity.isAdmin() {
  204. r.Header.Set(s3_constants.AmzIsAdmin, "true")
  205. } else if _, ok := r.Header[s3_constants.AmzIsAdmin]; ok {
  206. r.Header.Del(s3_constants.AmzIsAdmin)
  207. }
  208. }
  209. f(w, r)
  210. return
  211. }
  212. s3err.WriteErrorResponse(w, r, errCode)
  213. }
  214. }
  215. func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) (*Identity, s3err.ErrorCode) {
  216. return authRequest(iam, nil, r, action, false)
  217. }
  218. func authRequest(iam *IdentityAccessManagement, br *BucketRegistry, r *http.Request, action Action, supportAcl bool) (*Identity, s3err.ErrorCode) {
  219. var identity *Identity
  220. var s3Err s3err.ErrorCode
  221. var authType string
  222. switch getRequestAuthType(r) {
  223. case authTypeStreamingSigned:
  224. return identity, s3err.ErrNone
  225. case authTypeUnknown:
  226. glog.V(3).Infof("unknown auth type")
  227. r.Header.Set(s3_constants.AmzAuthType, "Unknown")
  228. return identity, s3err.ErrAccessDenied
  229. case authTypePresignedV2, authTypeSignedV2:
  230. glog.V(3).Infof("v2 auth type")
  231. identity, s3Err = iam.isReqAuthenticatedV2(r)
  232. authType = "SigV2"
  233. case authTypeSigned, authTypePresigned:
  234. glog.V(3).Infof("v4 auth type")
  235. identity, s3Err = iam.reqSignatureV4Verify(r)
  236. authType = "SigV4"
  237. case authTypePostPolicy:
  238. glog.V(3).Infof("post policy auth type")
  239. r.Header.Set(s3_constants.AmzAuthType, "PostPolicy")
  240. return identity, s3err.ErrNone
  241. case authTypeJWT:
  242. glog.V(3).Infof("jwt auth type")
  243. r.Header.Set(s3_constants.AmzAuthType, "Jwt")
  244. return identity, s3err.ErrNotImplemented
  245. case authTypeAnonymous:
  246. if supportAcl && br != nil {
  247. bucket, _ := s3_constants.GetBucketAndObject(r)
  248. bucketMetadata, errorCode := br.GetBucketMetadata(bucket)
  249. if errorCode != s3err.ErrNone {
  250. return nil, errorCode
  251. }
  252. if bucketMetadata.ObjectOwnership != s3_constants.OwnershipBucketOwnerEnforced {
  253. return IdentityAnonymous, s3err.ErrNone
  254. }
  255. }
  256. authType = "Anonymous"
  257. identity = IdentityAnonymous
  258. if len(identity.Actions) == 0 {
  259. r.Header.Set(s3_constants.AmzAuthType, authType)
  260. return identity, s3err.ErrAccessDenied
  261. }
  262. default:
  263. return identity, s3err.ErrNotImplemented
  264. }
  265. if len(authType) > 0 {
  266. r.Header.Set(s3_constants.AmzAuthType, authType)
  267. }
  268. if s3Err != s3err.ErrNone {
  269. return identity, s3Err
  270. }
  271. glog.V(3).Infof("user name: %v account id: %v actions: %v, action: %v", identity.Name, identity.AccountId, identity.Actions, action)
  272. bucket, object := s3_constants.GetBucketAndObject(r)
  273. if !identity.canDo(action, bucket, object) {
  274. return identity, s3err.ErrAccessDenied
  275. }
  276. if !identity.isAnonymous() {
  277. r.Header.Set(s3_constants.AmzAccountId, identity.AccountId)
  278. }
  279. return identity, s3err.ErrNone
  280. }
  281. func (iam *IdentityAccessManagement) authUser(r *http.Request) (*Identity, s3err.ErrorCode) {
  282. var identity *Identity
  283. var s3Err s3err.ErrorCode
  284. var found bool
  285. var authType string
  286. switch getRequestAuthType(r) {
  287. case authTypeStreamingSigned:
  288. return identity, s3err.ErrNone
  289. case authTypeUnknown:
  290. glog.V(3).Infof("unknown auth type")
  291. r.Header.Set(s3_constants.AmzAuthType, "Unknown")
  292. return identity, s3err.ErrAccessDenied
  293. case authTypePresignedV2, authTypeSignedV2:
  294. glog.V(3).Infof("v2 auth type")
  295. identity, s3Err = iam.isReqAuthenticatedV2(r)
  296. authType = "SigV2"
  297. case authTypeSigned, authTypePresigned:
  298. glog.V(3).Infof("v4 auth type")
  299. identity, s3Err = iam.reqSignatureV4Verify(r)
  300. authType = "SigV4"
  301. case authTypePostPolicy:
  302. glog.V(3).Infof("post policy auth type")
  303. r.Header.Set(s3_constants.AmzAuthType, "PostPolicy")
  304. return identity, s3err.ErrNone
  305. case authTypeJWT:
  306. glog.V(3).Infof("jwt auth type")
  307. r.Header.Set(s3_constants.AmzAuthType, "Jwt")
  308. return identity, s3err.ErrNotImplemented
  309. case authTypeAnonymous:
  310. authType = "Anonymous"
  311. identity, found = iam.lookupAnonymous()
  312. if !found {
  313. r.Header.Set(s3_constants.AmzAuthType, authType)
  314. return identity, s3err.ErrAccessDenied
  315. }
  316. default:
  317. return identity, s3err.ErrNotImplemented
  318. }
  319. if len(authType) > 0 {
  320. r.Header.Set(s3_constants.AmzAuthType, authType)
  321. }
  322. glog.V(3).Infof("auth error: %v", s3Err)
  323. if s3Err != s3err.ErrNone {
  324. return identity, s3Err
  325. }
  326. return identity, s3err.ErrNone
  327. }
  328. func (identity *Identity) canDo(action Action, bucket string, objectKey string) bool {
  329. if identity.isAdmin() {
  330. return true
  331. }
  332. for _, a := range identity.Actions {
  333. if a == action {
  334. return true
  335. }
  336. }
  337. if bucket == "" {
  338. return false
  339. }
  340. target := string(action) + ":" + bucket + objectKey
  341. adminTarget := s3_constants.ACTION_ADMIN + ":" + bucket + objectKey
  342. limitedByBucket := string(action) + ":" + bucket
  343. adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket
  344. for _, a := range identity.Actions {
  345. act := string(a)
  346. if strings.HasSuffix(act, "*") {
  347. if strings.HasPrefix(target, act[:len(act)-1]) {
  348. return true
  349. }
  350. if strings.HasPrefix(adminTarget, act[:len(act)-1]) {
  351. return true
  352. }
  353. } else {
  354. if act == limitedByBucket {
  355. return true
  356. }
  357. if act == adminLimitedByBucket {
  358. return true
  359. }
  360. }
  361. }
  362. return false
  363. }
  364. func (identity *Identity) isAdmin() bool {
  365. for _, a := range identity.Actions {
  366. if a == "Admin" {
  367. return true
  368. }
  369. }
  370. return false
  371. }