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.
618 lines
17 KiB
618 lines
17 KiB
package s3api
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/iam/policy"
|
|
)
|
|
|
|
// S3PolicyTemplates provides pre-built IAM policy templates for common S3 use cases
|
|
type S3PolicyTemplates struct{}
|
|
|
|
// NewS3PolicyTemplates creates a new policy templates provider
|
|
func NewS3PolicyTemplates() *S3PolicyTemplates {
|
|
return &S3PolicyTemplates{}
|
|
}
|
|
|
|
// GetS3ReadOnlyPolicy returns a policy that allows read-only access to all S3 resources
|
|
func (t *S3PolicyTemplates) GetS3ReadOnlyPolicy() *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "S3ReadOnlyAccess",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:GetObject",
|
|
"s3:GetObjectVersion",
|
|
"s3:ListBucket",
|
|
"s3:ListBucketVersions",
|
|
"s3:GetBucketLocation",
|
|
"s3:GetBucketVersioning",
|
|
"s3:ListAllMyBuckets",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::*",
|
|
"arn:seaweed:s3:::*/*",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetS3WriteOnlyPolicy returns a policy that allows write-only access to all S3 resources
|
|
func (t *S3PolicyTemplates) GetS3WriteOnlyPolicy() *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "S3WriteOnlyAccess",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:PutObject",
|
|
"s3:PutObjectAcl",
|
|
"s3:CreateMultipartUpload",
|
|
"s3:UploadPart",
|
|
"s3:CompleteMultipartUpload",
|
|
"s3:AbortMultipartUpload",
|
|
"s3:ListMultipartUploads",
|
|
"s3:ListParts",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::*",
|
|
"arn:seaweed:s3:::*/*",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetS3AdminPolicy returns a policy that allows full admin access to all S3 resources
|
|
func (t *S3PolicyTemplates) GetS3AdminPolicy() *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "S3FullAccess",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:*",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::*",
|
|
"arn:seaweed:s3:::*/*",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetBucketSpecificReadPolicy returns a policy for read-only access to a specific bucket
|
|
func (t *S3PolicyTemplates) GetBucketSpecificReadPolicy(bucketName string) *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "BucketSpecificReadAccess",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:GetObject",
|
|
"s3:GetObjectVersion",
|
|
"s3:ListBucket",
|
|
"s3:ListBucketVersions",
|
|
"s3:GetBucketLocation",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName,
|
|
"arn:seaweed:s3:::" + bucketName + "/*",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetBucketSpecificWritePolicy returns a policy for write-only access to a specific bucket
|
|
func (t *S3PolicyTemplates) GetBucketSpecificWritePolicy(bucketName string) *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "BucketSpecificWriteAccess",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:PutObject",
|
|
"s3:PutObjectAcl",
|
|
"s3:CreateMultipartUpload",
|
|
"s3:UploadPart",
|
|
"s3:CompleteMultipartUpload",
|
|
"s3:AbortMultipartUpload",
|
|
"s3:ListMultipartUploads",
|
|
"s3:ListParts",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName,
|
|
"arn:seaweed:s3:::" + bucketName + "/*",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetPathBasedAccessPolicy returns a policy that restricts access to a specific path within a bucket
|
|
func (t *S3PolicyTemplates) GetPathBasedAccessPolicy(bucketName, pathPrefix string) *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "ListBucketPermission",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:ListBucket",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName,
|
|
},
|
|
Condition: map[string]map[string]interface{}{
|
|
"StringLike": map[string]interface{}{
|
|
"s3:prefix": []string{pathPrefix + "/*"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Sid: "PathBasedObjectAccess",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:GetObject",
|
|
"s3:PutObject",
|
|
"s3:DeleteObject",
|
|
"s3:CreateMultipartUpload",
|
|
"s3:UploadPart",
|
|
"s3:CompleteMultipartUpload",
|
|
"s3:AbortMultipartUpload",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName + "/" + pathPrefix + "/*",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetIPRestrictedPolicy returns a policy that restricts access based on source IP
|
|
func (t *S3PolicyTemplates) GetIPRestrictedPolicy(allowedCIDRs []string) *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "IPRestrictedS3Access",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:*",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::*",
|
|
"arn:seaweed:s3:::*/*",
|
|
},
|
|
Condition: map[string]map[string]interface{}{
|
|
"IpAddress": map[string]interface{}{
|
|
"aws:SourceIp": allowedCIDRs,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetTimeBasedAccessPolicy returns a policy that allows access only during specific hours
|
|
func (t *S3PolicyTemplates) GetTimeBasedAccessPolicy(startHour, endHour int) *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "TimeBasedS3Access",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:GetObject",
|
|
"s3:PutObject",
|
|
"s3:ListBucket",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::*",
|
|
"arn:seaweed:s3:::*/*",
|
|
},
|
|
Condition: map[string]map[string]interface{}{
|
|
"DateGreaterThan": map[string]interface{}{
|
|
"aws:CurrentTime": time.Now().Format("2006-01-02") + "T" +
|
|
formatHour(startHour) + ":00:00Z",
|
|
},
|
|
"DateLessThan": map[string]interface{}{
|
|
"aws:CurrentTime": time.Now().Format("2006-01-02") + "T" +
|
|
formatHour(endHour) + ":00:00Z",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetMultipartUploadPolicy returns a policy specifically for multipart upload operations
|
|
func (t *S3PolicyTemplates) GetMultipartUploadPolicy(bucketName string) *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "MultipartUploadOperations",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:CreateMultipartUpload",
|
|
"s3:UploadPart",
|
|
"s3:CompleteMultipartUpload",
|
|
"s3:AbortMultipartUpload",
|
|
"s3:ListMultipartUploads",
|
|
"s3:ListParts",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName + "/*",
|
|
},
|
|
},
|
|
{
|
|
Sid: "ListBucketForMultipart",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:ListBucket",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetPresignedURLPolicy returns a policy for generating and using presigned URLs
|
|
func (t *S3PolicyTemplates) GetPresignedURLPolicy(bucketName string) *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "PresignedURLAccess",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:GetObject",
|
|
"s3:PutObject",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName + "/*",
|
|
},
|
|
Condition: map[string]map[string]interface{}{
|
|
"StringEquals": map[string]interface{}{
|
|
"s3:x-amz-signature-version": "AWS4-HMAC-SHA256",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetTemporaryAccessPolicy returns a policy for temporary access with expiration
|
|
func (t *S3PolicyTemplates) GetTemporaryAccessPolicy(bucketName string, expirationHours int) *policy.PolicyDocument {
|
|
expirationTime := time.Now().Add(time.Duration(expirationHours) * time.Hour)
|
|
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "TemporaryS3Access",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:GetObject",
|
|
"s3:PutObject",
|
|
"s3:ListBucket",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName,
|
|
"arn:seaweed:s3:::" + bucketName + "/*",
|
|
},
|
|
Condition: map[string]map[string]interface{}{
|
|
"DateLessThan": map[string]interface{}{
|
|
"aws:CurrentTime": expirationTime.UTC().Format("2006-01-02T15:04:05Z"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetContentTypeRestrictedPolicy returns a policy that restricts uploads to specific content types
|
|
func (t *S3PolicyTemplates) GetContentTypeRestrictedPolicy(bucketName string, allowedContentTypes []string) *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "ContentTypeRestrictedUpload",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:PutObject",
|
|
"s3:CreateMultipartUpload",
|
|
"s3:UploadPart",
|
|
"s3:CompleteMultipartUpload",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName + "/*",
|
|
},
|
|
Condition: map[string]map[string]interface{}{
|
|
"StringEquals": map[string]interface{}{
|
|
"s3:content-type": allowedContentTypes,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Sid: "ReadAccess",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:GetObject",
|
|
"s3:ListBucket",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::" + bucketName,
|
|
"arn:seaweed:s3:::" + bucketName + "/*",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetDenyDeletePolicy returns a policy that allows all operations except delete
|
|
func (t *S3PolicyTemplates) GetDenyDeletePolicy() *policy.PolicyDocument {
|
|
return &policy.PolicyDocument{
|
|
Version: "2012-10-17",
|
|
Statement: []policy.Statement{
|
|
{
|
|
Sid: "AllowAllExceptDelete",
|
|
Effect: "Allow",
|
|
Action: []string{
|
|
"s3:GetObject",
|
|
"s3:GetObjectVersion",
|
|
"s3:PutObject",
|
|
"s3:PutObjectAcl",
|
|
"s3:ListBucket",
|
|
"s3:ListBucketVersions",
|
|
"s3:CreateMultipartUpload",
|
|
"s3:UploadPart",
|
|
"s3:CompleteMultipartUpload",
|
|
"s3:AbortMultipartUpload",
|
|
"s3:ListMultipartUploads",
|
|
"s3:ListParts",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::*",
|
|
"arn:seaweed:s3:::*/*",
|
|
},
|
|
},
|
|
{
|
|
Sid: "DenyDeleteOperations",
|
|
Effect: "Deny",
|
|
Action: []string{
|
|
"s3:DeleteObject",
|
|
"s3:DeleteObjectVersion",
|
|
"s3:DeleteBucket",
|
|
},
|
|
Resource: []string{
|
|
"arn:seaweed:s3:::*",
|
|
"arn:seaweed:s3:::*/*",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// Helper function to format hour with leading zero
|
|
func formatHour(hour int) string {
|
|
if hour < 10 {
|
|
return "0" + string(rune('0'+hour))
|
|
}
|
|
return string(rune('0'+hour/10)) + string(rune('0'+hour%10))
|
|
}
|
|
|
|
// PolicyTemplateDefinition represents metadata about a policy template
|
|
type PolicyTemplateDefinition struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Category string `json:"category"`
|
|
UseCase string `json:"use_case"`
|
|
Parameters []PolicyTemplateParam `json:"parameters,omitempty"`
|
|
Policy *policy.PolicyDocument `json:"policy"`
|
|
}
|
|
|
|
// PolicyTemplateParam represents a parameter for customizing policy templates
|
|
type PolicyTemplateParam struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Description string `json:"description"`
|
|
Required bool `json:"required"`
|
|
DefaultValue string `json:"default_value,omitempty"`
|
|
Example string `json:"example,omitempty"`
|
|
}
|
|
|
|
// GetAllPolicyTemplates returns all available policy templates with metadata
|
|
func (t *S3PolicyTemplates) GetAllPolicyTemplates() []PolicyTemplateDefinition {
|
|
return []PolicyTemplateDefinition{
|
|
{
|
|
Name: "S3ReadOnlyAccess",
|
|
Description: "Provides read-only access to all S3 buckets and objects",
|
|
Category: "Basic Access",
|
|
UseCase: "Data consumers, backup services, monitoring applications",
|
|
Policy: t.GetS3ReadOnlyPolicy(),
|
|
},
|
|
{
|
|
Name: "S3WriteOnlyAccess",
|
|
Description: "Provides write-only access to all S3 buckets and objects",
|
|
Category: "Basic Access",
|
|
UseCase: "Data ingestion services, backup applications",
|
|
Policy: t.GetS3WriteOnlyPolicy(),
|
|
},
|
|
{
|
|
Name: "S3AdminAccess",
|
|
Description: "Provides full administrative access to all S3 resources",
|
|
Category: "Administrative",
|
|
UseCase: "S3 administrators, service accounts with full control",
|
|
Policy: t.GetS3AdminPolicy(),
|
|
},
|
|
{
|
|
Name: "BucketSpecificRead",
|
|
Description: "Provides read-only access to a specific bucket",
|
|
Category: "Bucket-Specific",
|
|
UseCase: "Applications that need access to specific data sets",
|
|
Parameters: []PolicyTemplateParam{
|
|
{
|
|
Name: "bucketName",
|
|
Type: "string",
|
|
Description: "Name of the S3 bucket to grant access to",
|
|
Required: true,
|
|
Example: "my-data-bucket",
|
|
},
|
|
},
|
|
Policy: t.GetBucketSpecificReadPolicy("${bucketName}"),
|
|
},
|
|
{
|
|
Name: "BucketSpecificWrite",
|
|
Description: "Provides write-only access to a specific bucket",
|
|
Category: "Bucket-Specific",
|
|
UseCase: "Upload services, data ingestion for specific datasets",
|
|
Parameters: []PolicyTemplateParam{
|
|
{
|
|
Name: "bucketName",
|
|
Type: "string",
|
|
Description: "Name of the S3 bucket to grant access to",
|
|
Required: true,
|
|
Example: "my-upload-bucket",
|
|
},
|
|
},
|
|
Policy: t.GetBucketSpecificWritePolicy("${bucketName}"),
|
|
},
|
|
{
|
|
Name: "PathBasedAccess",
|
|
Description: "Restricts access to a specific path/prefix within a bucket",
|
|
Category: "Path-Restricted",
|
|
UseCase: "Multi-tenant applications, user-specific directories",
|
|
Parameters: []PolicyTemplateParam{
|
|
{
|
|
Name: "bucketName",
|
|
Type: "string",
|
|
Description: "Name of the S3 bucket",
|
|
Required: true,
|
|
Example: "shared-bucket",
|
|
},
|
|
{
|
|
Name: "pathPrefix",
|
|
Type: "string",
|
|
Description: "Path prefix to restrict access to",
|
|
Required: true,
|
|
Example: "user123/documents",
|
|
},
|
|
},
|
|
Policy: t.GetPathBasedAccessPolicy("${bucketName}", "${pathPrefix}"),
|
|
},
|
|
{
|
|
Name: "IPRestrictedAccess",
|
|
Description: "Allows access only from specific IP addresses or ranges",
|
|
Category: "Security",
|
|
UseCase: "Corporate networks, office-based access, VPN restrictions",
|
|
Parameters: []PolicyTemplateParam{
|
|
{
|
|
Name: "allowedCIDRs",
|
|
Type: "array",
|
|
Description: "List of allowed IP addresses or CIDR ranges",
|
|
Required: true,
|
|
Example: "[\"192.168.1.0/24\", \"10.0.0.0/8\"]",
|
|
},
|
|
},
|
|
Policy: t.GetIPRestrictedPolicy([]string{"${allowedCIDRs}"}),
|
|
},
|
|
{
|
|
Name: "MultipartUploadOnly",
|
|
Description: "Allows only multipart upload operations on a specific bucket",
|
|
Category: "Upload-Specific",
|
|
UseCase: "Large file upload services, streaming applications",
|
|
Parameters: []PolicyTemplateParam{
|
|
{
|
|
Name: "bucketName",
|
|
Type: "string",
|
|
Description: "Name of the S3 bucket for multipart uploads",
|
|
Required: true,
|
|
Example: "large-files-bucket",
|
|
},
|
|
},
|
|
Policy: t.GetMultipartUploadPolicy("${bucketName}"),
|
|
},
|
|
{
|
|
Name: "PresignedURLAccess",
|
|
Description: "Policy for generating and using presigned URLs",
|
|
Category: "Presigned URLs",
|
|
UseCase: "Frontend applications, temporary file sharing",
|
|
Parameters: []PolicyTemplateParam{
|
|
{
|
|
Name: "bucketName",
|
|
Type: "string",
|
|
Description: "Name of the S3 bucket for presigned URL access",
|
|
Required: true,
|
|
Example: "shared-files-bucket",
|
|
},
|
|
},
|
|
Policy: t.GetPresignedURLPolicy("${bucketName}"),
|
|
},
|
|
{
|
|
Name: "ContentTypeRestricted",
|
|
Description: "Restricts uploads to specific content types",
|
|
Category: "Content Control",
|
|
UseCase: "Image galleries, document repositories, media libraries",
|
|
Parameters: []PolicyTemplateParam{
|
|
{
|
|
Name: "bucketName",
|
|
Type: "string",
|
|
Description: "Name of the S3 bucket",
|
|
Required: true,
|
|
Example: "media-bucket",
|
|
},
|
|
{
|
|
Name: "allowedContentTypes",
|
|
Type: "array",
|
|
Description: "List of allowed MIME content types",
|
|
Required: true,
|
|
Example: "[\"image/jpeg\", \"image/png\", \"video/mp4\"]",
|
|
},
|
|
},
|
|
Policy: t.GetContentTypeRestrictedPolicy("${bucketName}", []string{"${allowedContentTypes}"}),
|
|
},
|
|
{
|
|
Name: "DenyDeleteAccess",
|
|
Description: "Allows all operations except delete (immutable storage)",
|
|
Category: "Data Protection",
|
|
UseCase: "Compliance storage, audit logs, backup retention",
|
|
Policy: t.GetDenyDeletePolicy(),
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetPolicyTemplateByName returns a specific policy template by name
|
|
func (t *S3PolicyTemplates) GetPolicyTemplateByName(name string) *PolicyTemplateDefinition {
|
|
templates := t.GetAllPolicyTemplates()
|
|
for _, template := range templates {
|
|
if template.Name == name {
|
|
return &template
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetPolicyTemplatesByCategory returns all policy templates in a specific category
|
|
func (t *S3PolicyTemplates) GetPolicyTemplatesByCategory(category string) []PolicyTemplateDefinition {
|
|
var result []PolicyTemplateDefinition
|
|
templates := t.GetAllPolicyTemplates()
|
|
for _, template := range templates {
|
|
if template.Category == category {
|
|
result = append(result, template)
|
|
}
|
|
}
|
|
return result
|
|
}
|