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.

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