s3_policy_templates.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. package s3api
  2. import (
  3. "time"
  4. "github.com/seaweedfs/seaweedfs/weed/iam/policy"
  5. )
  6. // S3PolicyTemplates provides pre-built IAM policy templates for common S3 use cases
  7. type S3PolicyTemplates struct{}
  8. // NewS3PolicyTemplates creates a new policy templates provider
  9. func NewS3PolicyTemplates() *S3PolicyTemplates {
  10. return &S3PolicyTemplates{}
  11. }
  12. // GetS3ReadOnlyPolicy returns a policy that allows read-only access to all S3 resources
  13. func (t *S3PolicyTemplates) GetS3ReadOnlyPolicy() *policy.PolicyDocument {
  14. return &policy.PolicyDocument{
  15. Version: "2012-10-17",
  16. Statement: []policy.Statement{
  17. {
  18. Sid: "S3ReadOnlyAccess",
  19. Effect: "Allow",
  20. Action: []string{
  21. "s3:GetObject",
  22. "s3:GetObjectVersion",
  23. "s3:ListBucket",
  24. "s3:ListBucketVersions",
  25. "s3:GetBucketLocation",
  26. "s3:GetBucketVersioning",
  27. "s3:ListAllMyBuckets",
  28. },
  29. Resource: []string{
  30. "arn:seaweed:s3:::*",
  31. "arn:seaweed:s3:::*/*",
  32. },
  33. },
  34. },
  35. }
  36. }
  37. // GetS3WriteOnlyPolicy returns a policy that allows write-only access to all S3 resources
  38. func (t *S3PolicyTemplates) GetS3WriteOnlyPolicy() *policy.PolicyDocument {
  39. return &policy.PolicyDocument{
  40. Version: "2012-10-17",
  41. Statement: []policy.Statement{
  42. {
  43. Sid: "S3WriteOnlyAccess",
  44. Effect: "Allow",
  45. Action: []string{
  46. "s3:PutObject",
  47. "s3:PutObjectAcl",
  48. "s3:CreateMultipartUpload",
  49. "s3:UploadPart",
  50. "s3:CompleteMultipartUpload",
  51. "s3:AbortMultipartUpload",
  52. "s3:ListMultipartUploads",
  53. "s3:ListParts",
  54. },
  55. Resource: []string{
  56. "arn:seaweed:s3:::*",
  57. "arn:seaweed:s3:::*/*",
  58. },
  59. },
  60. },
  61. }
  62. }
  63. // GetS3AdminPolicy returns a policy that allows full admin access to all S3 resources
  64. func (t *S3PolicyTemplates) GetS3AdminPolicy() *policy.PolicyDocument {
  65. return &policy.PolicyDocument{
  66. Version: "2012-10-17",
  67. Statement: []policy.Statement{
  68. {
  69. Sid: "S3FullAccess",
  70. Effect: "Allow",
  71. Action: []string{
  72. "s3:*",
  73. },
  74. Resource: []string{
  75. "arn:seaweed:s3:::*",
  76. "arn:seaweed:s3:::*/*",
  77. },
  78. },
  79. },
  80. }
  81. }
  82. // GetBucketSpecificReadPolicy returns a policy for read-only access to a specific bucket
  83. func (t *S3PolicyTemplates) GetBucketSpecificReadPolicy(bucketName string) *policy.PolicyDocument {
  84. return &policy.PolicyDocument{
  85. Version: "2012-10-17",
  86. Statement: []policy.Statement{
  87. {
  88. Sid: "BucketSpecificReadAccess",
  89. Effect: "Allow",
  90. Action: []string{
  91. "s3:GetObject",
  92. "s3:GetObjectVersion",
  93. "s3:ListBucket",
  94. "s3:ListBucketVersions",
  95. "s3:GetBucketLocation",
  96. },
  97. Resource: []string{
  98. "arn:seaweed:s3:::" + bucketName,
  99. "arn:seaweed:s3:::" + bucketName + "/*",
  100. },
  101. },
  102. },
  103. }
  104. }
  105. // GetBucketSpecificWritePolicy returns a policy for write-only access to a specific bucket
  106. func (t *S3PolicyTemplates) GetBucketSpecificWritePolicy(bucketName string) *policy.PolicyDocument {
  107. return &policy.PolicyDocument{
  108. Version: "2012-10-17",
  109. Statement: []policy.Statement{
  110. {
  111. Sid: "BucketSpecificWriteAccess",
  112. Effect: "Allow",
  113. Action: []string{
  114. "s3:PutObject",
  115. "s3:PutObjectAcl",
  116. "s3:CreateMultipartUpload",
  117. "s3:UploadPart",
  118. "s3:CompleteMultipartUpload",
  119. "s3:AbortMultipartUpload",
  120. "s3:ListMultipartUploads",
  121. "s3:ListParts",
  122. },
  123. Resource: []string{
  124. "arn:seaweed:s3:::" + bucketName,
  125. "arn:seaweed:s3:::" + bucketName + "/*",
  126. },
  127. },
  128. },
  129. }
  130. }
  131. // GetPathBasedAccessPolicy returns a policy that restricts access to a specific path within a bucket
  132. func (t *S3PolicyTemplates) GetPathBasedAccessPolicy(bucketName, pathPrefix string) *policy.PolicyDocument {
  133. return &policy.PolicyDocument{
  134. Version: "2012-10-17",
  135. Statement: []policy.Statement{
  136. {
  137. Sid: "ListBucketPermission",
  138. Effect: "Allow",
  139. Action: []string{
  140. "s3:ListBucket",
  141. },
  142. Resource: []string{
  143. "arn:seaweed:s3:::" + bucketName,
  144. },
  145. Condition: map[string]map[string]interface{}{
  146. "StringLike": map[string]interface{}{
  147. "s3:prefix": []string{pathPrefix + "/*"},
  148. },
  149. },
  150. },
  151. {
  152. Sid: "PathBasedObjectAccess",
  153. Effect: "Allow",
  154. Action: []string{
  155. "s3:GetObject",
  156. "s3:PutObject",
  157. "s3:DeleteObject",
  158. "s3:CreateMultipartUpload",
  159. "s3:UploadPart",
  160. "s3:CompleteMultipartUpload",
  161. "s3:AbortMultipartUpload",
  162. },
  163. Resource: []string{
  164. "arn:seaweed:s3:::" + bucketName + "/" + pathPrefix + "/*",
  165. },
  166. },
  167. },
  168. }
  169. }
  170. // GetIPRestrictedPolicy returns a policy that restricts access based on source IP
  171. func (t *S3PolicyTemplates) GetIPRestrictedPolicy(allowedCIDRs []string) *policy.PolicyDocument {
  172. return &policy.PolicyDocument{
  173. Version: "2012-10-17",
  174. Statement: []policy.Statement{
  175. {
  176. Sid: "IPRestrictedS3Access",
  177. Effect: "Allow",
  178. Action: []string{
  179. "s3:*",
  180. },
  181. Resource: []string{
  182. "arn:seaweed:s3:::*",
  183. "arn:seaweed:s3:::*/*",
  184. },
  185. Condition: map[string]map[string]interface{}{
  186. "IpAddress": map[string]interface{}{
  187. "aws:SourceIp": allowedCIDRs,
  188. },
  189. },
  190. },
  191. },
  192. }
  193. }
  194. // GetTimeBasedAccessPolicy returns a policy that allows access only during specific hours
  195. func (t *S3PolicyTemplates) GetTimeBasedAccessPolicy(startHour, endHour int) *policy.PolicyDocument {
  196. return &policy.PolicyDocument{
  197. Version: "2012-10-17",
  198. Statement: []policy.Statement{
  199. {
  200. Sid: "TimeBasedS3Access",
  201. Effect: "Allow",
  202. Action: []string{
  203. "s3:GetObject",
  204. "s3:PutObject",
  205. "s3:ListBucket",
  206. },
  207. Resource: []string{
  208. "arn:seaweed:s3:::*",
  209. "arn:seaweed:s3:::*/*",
  210. },
  211. Condition: map[string]map[string]interface{}{
  212. "DateGreaterThan": map[string]interface{}{
  213. "aws:CurrentTime": time.Now().Format("2006-01-02") + "T" +
  214. formatHour(startHour) + ":00:00Z",
  215. },
  216. "DateLessThan": map[string]interface{}{
  217. "aws:CurrentTime": time.Now().Format("2006-01-02") + "T" +
  218. formatHour(endHour) + ":00:00Z",
  219. },
  220. },
  221. },
  222. },
  223. }
  224. }
  225. // GetMultipartUploadPolicy returns a policy specifically for multipart upload operations
  226. func (t *S3PolicyTemplates) GetMultipartUploadPolicy(bucketName string) *policy.PolicyDocument {
  227. return &policy.PolicyDocument{
  228. Version: "2012-10-17",
  229. Statement: []policy.Statement{
  230. {
  231. Sid: "MultipartUploadOperations",
  232. Effect: "Allow",
  233. Action: []string{
  234. "s3:CreateMultipartUpload",
  235. "s3:UploadPart",
  236. "s3:CompleteMultipartUpload",
  237. "s3:AbortMultipartUpload",
  238. "s3:ListMultipartUploads",
  239. "s3:ListParts",
  240. },
  241. Resource: []string{
  242. "arn:seaweed:s3:::" + bucketName + "/*",
  243. },
  244. },
  245. {
  246. Sid: "ListBucketForMultipart",
  247. Effect: "Allow",
  248. Action: []string{
  249. "s3:ListBucket",
  250. },
  251. Resource: []string{
  252. "arn:seaweed:s3:::" + bucketName,
  253. },
  254. },
  255. },
  256. }
  257. }
  258. // GetPresignedURLPolicy returns a policy for generating and using presigned URLs
  259. func (t *S3PolicyTemplates) GetPresignedURLPolicy(bucketName string) *policy.PolicyDocument {
  260. return &policy.PolicyDocument{
  261. Version: "2012-10-17",
  262. Statement: []policy.Statement{
  263. {
  264. Sid: "PresignedURLAccess",
  265. Effect: "Allow",
  266. Action: []string{
  267. "s3:GetObject",
  268. "s3:PutObject",
  269. },
  270. Resource: []string{
  271. "arn:seaweed:s3:::" + bucketName + "/*",
  272. },
  273. Condition: map[string]map[string]interface{}{
  274. "StringEquals": map[string]interface{}{
  275. "s3:x-amz-signature-version": "AWS4-HMAC-SHA256",
  276. },
  277. },
  278. },
  279. },
  280. }
  281. }
  282. // GetTemporaryAccessPolicy returns a policy for temporary access with expiration
  283. func (t *S3PolicyTemplates) GetTemporaryAccessPolicy(bucketName string, expirationHours int) *policy.PolicyDocument {
  284. expirationTime := time.Now().Add(time.Duration(expirationHours) * time.Hour)
  285. return &policy.PolicyDocument{
  286. Version: "2012-10-17",
  287. Statement: []policy.Statement{
  288. {
  289. Sid: "TemporaryS3Access",
  290. Effect: "Allow",
  291. Action: []string{
  292. "s3:GetObject",
  293. "s3:PutObject",
  294. "s3:ListBucket",
  295. },
  296. Resource: []string{
  297. "arn:seaweed:s3:::" + bucketName,
  298. "arn:seaweed:s3:::" + bucketName + "/*",
  299. },
  300. Condition: map[string]map[string]interface{}{
  301. "DateLessThan": map[string]interface{}{
  302. "aws:CurrentTime": expirationTime.UTC().Format("2006-01-02T15:04:05Z"),
  303. },
  304. },
  305. },
  306. },
  307. }
  308. }
  309. // GetContentTypeRestrictedPolicy returns a policy that restricts uploads to specific content types
  310. func (t *S3PolicyTemplates) GetContentTypeRestrictedPolicy(bucketName string, allowedContentTypes []string) *policy.PolicyDocument {
  311. return &policy.PolicyDocument{
  312. Version: "2012-10-17",
  313. Statement: []policy.Statement{
  314. {
  315. Sid: "ContentTypeRestrictedUpload",
  316. Effect: "Allow",
  317. Action: []string{
  318. "s3:PutObject",
  319. "s3:CreateMultipartUpload",
  320. "s3:UploadPart",
  321. "s3:CompleteMultipartUpload",
  322. },
  323. Resource: []string{
  324. "arn:seaweed:s3:::" + bucketName + "/*",
  325. },
  326. Condition: map[string]map[string]interface{}{
  327. "StringEquals": map[string]interface{}{
  328. "s3:content-type": allowedContentTypes,
  329. },
  330. },
  331. },
  332. {
  333. Sid: "ReadAccess",
  334. Effect: "Allow",
  335. Action: []string{
  336. "s3:GetObject",
  337. "s3:ListBucket",
  338. },
  339. Resource: []string{
  340. "arn:seaweed:s3:::" + bucketName,
  341. "arn:seaweed:s3:::" + bucketName + "/*",
  342. },
  343. },
  344. },
  345. }
  346. }
  347. // GetDenyDeletePolicy returns a policy that allows all operations except delete
  348. func (t *S3PolicyTemplates) GetDenyDeletePolicy() *policy.PolicyDocument {
  349. return &policy.PolicyDocument{
  350. Version: "2012-10-17",
  351. Statement: []policy.Statement{
  352. {
  353. Sid: "AllowAllExceptDelete",
  354. Effect: "Allow",
  355. Action: []string{
  356. "s3:GetObject",
  357. "s3:GetObjectVersion",
  358. "s3:PutObject",
  359. "s3:PutObjectAcl",
  360. "s3:ListBucket",
  361. "s3:ListBucketVersions",
  362. "s3:CreateMultipartUpload",
  363. "s3:UploadPart",
  364. "s3:CompleteMultipartUpload",
  365. "s3:AbortMultipartUpload",
  366. "s3:ListMultipartUploads",
  367. "s3:ListParts",
  368. },
  369. Resource: []string{
  370. "arn:seaweed:s3:::*",
  371. "arn:seaweed:s3:::*/*",
  372. },
  373. },
  374. {
  375. Sid: "DenyDeleteOperations",
  376. Effect: "Deny",
  377. Action: []string{
  378. "s3:DeleteObject",
  379. "s3:DeleteObjectVersion",
  380. "s3:DeleteBucket",
  381. },
  382. Resource: []string{
  383. "arn:seaweed:s3:::*",
  384. "arn:seaweed:s3:::*/*",
  385. },
  386. },
  387. },
  388. }
  389. }
  390. // Helper function to format hour with leading zero
  391. func formatHour(hour int) string {
  392. if hour < 10 {
  393. return "0" + string(rune('0'+hour))
  394. }
  395. return string(rune('0'+hour/10)) + string(rune('0'+hour%10))
  396. }
  397. // PolicyTemplateDefinition represents metadata about a policy template
  398. type PolicyTemplateDefinition struct {
  399. Name string `json:"name"`
  400. Description string `json:"description"`
  401. Category string `json:"category"`
  402. UseCase string `json:"use_case"`
  403. Parameters []PolicyTemplateParam `json:"parameters,omitempty"`
  404. Policy *policy.PolicyDocument `json:"policy"`
  405. }
  406. // PolicyTemplateParam represents a parameter for customizing policy templates
  407. type PolicyTemplateParam struct {
  408. Name string `json:"name"`
  409. Type string `json:"type"`
  410. Description string `json:"description"`
  411. Required bool `json:"required"`
  412. DefaultValue string `json:"default_value,omitempty"`
  413. Example string `json:"example,omitempty"`
  414. }
  415. // GetAllPolicyTemplates returns all available policy templates with metadata
  416. func (t *S3PolicyTemplates) GetAllPolicyTemplates() []PolicyTemplateDefinition {
  417. return []PolicyTemplateDefinition{
  418. {
  419. Name: "S3ReadOnlyAccess",
  420. Description: "Provides read-only access to all S3 buckets and objects",
  421. Category: "Basic Access",
  422. UseCase: "Data consumers, backup services, monitoring applications",
  423. Policy: t.GetS3ReadOnlyPolicy(),
  424. },
  425. {
  426. Name: "S3WriteOnlyAccess",
  427. Description: "Provides write-only access to all S3 buckets and objects",
  428. Category: "Basic Access",
  429. UseCase: "Data ingestion services, backup applications",
  430. Policy: t.GetS3WriteOnlyPolicy(),
  431. },
  432. {
  433. Name: "S3AdminAccess",
  434. Description: "Provides full administrative access to all S3 resources",
  435. Category: "Administrative",
  436. UseCase: "S3 administrators, service accounts with full control",
  437. Policy: t.GetS3AdminPolicy(),
  438. },
  439. {
  440. Name: "BucketSpecificRead",
  441. Description: "Provides read-only access to a specific bucket",
  442. Category: "Bucket-Specific",
  443. UseCase: "Applications that need access to specific data sets",
  444. Parameters: []PolicyTemplateParam{
  445. {
  446. Name: "bucketName",
  447. Type: "string",
  448. Description: "Name of the S3 bucket to grant access to",
  449. Required: true,
  450. Example: "my-data-bucket",
  451. },
  452. },
  453. Policy: t.GetBucketSpecificReadPolicy("${bucketName}"),
  454. },
  455. {
  456. Name: "BucketSpecificWrite",
  457. Description: "Provides write-only access to a specific bucket",
  458. Category: "Bucket-Specific",
  459. UseCase: "Upload services, data ingestion for specific datasets",
  460. Parameters: []PolicyTemplateParam{
  461. {
  462. Name: "bucketName",
  463. Type: "string",
  464. Description: "Name of the S3 bucket to grant access to",
  465. Required: true,
  466. Example: "my-upload-bucket",
  467. },
  468. },
  469. Policy: t.GetBucketSpecificWritePolicy("${bucketName}"),
  470. },
  471. {
  472. Name: "PathBasedAccess",
  473. Description: "Restricts access to a specific path/prefix within a bucket",
  474. Category: "Path-Restricted",
  475. UseCase: "Multi-tenant applications, user-specific directories",
  476. Parameters: []PolicyTemplateParam{
  477. {
  478. Name: "bucketName",
  479. Type: "string",
  480. Description: "Name of the S3 bucket",
  481. Required: true,
  482. Example: "shared-bucket",
  483. },
  484. {
  485. Name: "pathPrefix",
  486. Type: "string",
  487. Description: "Path prefix to restrict access to",
  488. Required: true,
  489. Example: "user123/documents",
  490. },
  491. },
  492. Policy: t.GetPathBasedAccessPolicy("${bucketName}", "${pathPrefix}"),
  493. },
  494. {
  495. Name: "IPRestrictedAccess",
  496. Description: "Allows access only from specific IP addresses or ranges",
  497. Category: "Security",
  498. UseCase: "Corporate networks, office-based access, VPN restrictions",
  499. Parameters: []PolicyTemplateParam{
  500. {
  501. Name: "allowedCIDRs",
  502. Type: "array",
  503. Description: "List of allowed IP addresses or CIDR ranges",
  504. Required: true,
  505. Example: "[\"192.168.1.0/24\", \"10.0.0.0/8\"]",
  506. },
  507. },
  508. Policy: t.GetIPRestrictedPolicy([]string{"${allowedCIDRs}"}),
  509. },
  510. {
  511. Name: "MultipartUploadOnly",
  512. Description: "Allows only multipart upload operations on a specific bucket",
  513. Category: "Upload-Specific",
  514. UseCase: "Large file upload services, streaming applications",
  515. Parameters: []PolicyTemplateParam{
  516. {
  517. Name: "bucketName",
  518. Type: "string",
  519. Description: "Name of the S3 bucket for multipart uploads",
  520. Required: true,
  521. Example: "large-files-bucket",
  522. },
  523. },
  524. Policy: t.GetMultipartUploadPolicy("${bucketName}"),
  525. },
  526. {
  527. Name: "PresignedURLAccess",
  528. Description: "Policy for generating and using presigned URLs",
  529. Category: "Presigned URLs",
  530. UseCase: "Frontend applications, temporary file sharing",
  531. Parameters: []PolicyTemplateParam{
  532. {
  533. Name: "bucketName",
  534. Type: "string",
  535. Description: "Name of the S3 bucket for presigned URL access",
  536. Required: true,
  537. Example: "shared-files-bucket",
  538. },
  539. },
  540. Policy: t.GetPresignedURLPolicy("${bucketName}"),
  541. },
  542. {
  543. Name: "ContentTypeRestricted",
  544. Description: "Restricts uploads to specific content types",
  545. Category: "Content Control",
  546. UseCase: "Image galleries, document repositories, media libraries",
  547. Parameters: []PolicyTemplateParam{
  548. {
  549. Name: "bucketName",
  550. Type: "string",
  551. Description: "Name of the S3 bucket",
  552. Required: true,
  553. Example: "media-bucket",
  554. },
  555. {
  556. Name: "allowedContentTypes",
  557. Type: "array",
  558. Description: "List of allowed MIME content types",
  559. Required: true,
  560. Example: "[\"image/jpeg\", \"image/png\", \"video/mp4\"]",
  561. },
  562. },
  563. Policy: t.GetContentTypeRestrictedPolicy("${bucketName}", []string{"${allowedContentTypes}"}),
  564. },
  565. {
  566. Name: "DenyDeleteAccess",
  567. Description: "Allows all operations except delete (immutable storage)",
  568. Category: "Data Protection",
  569. UseCase: "Compliance storage, audit logs, backup retention",
  570. Policy: t.GetDenyDeletePolicy(),
  571. },
  572. }
  573. }
  574. // GetPolicyTemplateByName returns a specific policy template by name
  575. func (t *S3PolicyTemplates) GetPolicyTemplateByName(name string) *PolicyTemplateDefinition {
  576. templates := t.GetAllPolicyTemplates()
  577. for _, template := range templates {
  578. if template.Name == name {
  579. return &template
  580. }
  581. }
  582. return nil
  583. }
  584. // GetPolicyTemplatesByCategory returns all policy templates in a specific category
  585. func (t *S3PolicyTemplates) GetPolicyTemplatesByCategory(category string) []PolicyTemplateDefinition {
  586. var result []PolicyTemplateDefinition
  587. templates := t.GetAllPolicyTemplates()
  588. for _, template := range templates {
  589. if template.Category == category {
  590. result = append(result, template)
  591. }
  592. }
  593. return result
  594. }