Files
s3/weed/s3api/policy_engine/INTEGRATION_EXAMPLE.md
zooqueen dd74d3e6d6 Replace AGPL MinIO fork with Apache-2.0 SeaweedFS fork; rebrand CLI weed->s3
hanzoai/s3 was an AGPL-3.0 MinIO fork whose network-copyleft blocks embedding
and reselling as a managed service. Replace it wholesale with a fork of
SeaweedFS (github.com/seaweedfs/seaweedfs, Apache-2.0, Go) which ships a
built-in S3 gateway + Filer.

- License: AGPL-3.0 -> Apache-2.0 (upstream LICENSE kept verbatim, incl.
  "Copyright 2025 Chris Lu"); NOTICE added crediting SeaweedFS. No AGPL/BUSL/
  SSPL/GPL or enterprise ("ee") files remain.
- Module: go.mod path set to github.com/hanzoai/s3 with a self-replace
  (replace github.com/seaweedfs/seaweedfs => ./) so the ~1800 internal Go files
  keep their upstream import paths with zero churn. Internal identifiers and
  import paths are intentionally NOT renamed.
- CLI rebrand: the binary is built as `s3` (was `weed`); root usage, per-command
  help/usage/flag strings, shell autocomplete program name, and the product name
  in CLI output now read "Hanzo S3" / `s3`. Users never see `weed`.
  (e.g. `s3 server`, `s3 filer`, `s3 master`, `s3 volume`, `s3 s3`, `s3 mount`.)
- Build: `make` (or `go build -o s3 ./weed`) builds the `s3` binary green;
  `cmd/weed-db` and `cmd/weed-sql` also build; `go vet ./weed/command/` clean.
- README/LLM.md rewritten for Hanzo S3; AGENTS.md/CLAUDE.md symlinks preserved.
- Dropped a 33MB prebuilt Mach-O `stress` binary upstream had committed; `.gitignore`
  updated to ignore the `s3` build output.
2026-06-18 23:41:25 -07:00

5.3 KiB

Integration Example

This shows how to integrate the new policy engine with the existing S3ApiServer.

Minimal Integration

// In s3api_server.go - modify NewS3ApiServerWithStore function

func NewS3ApiServerWithStore(router *mux.Router, option *S3ApiServerOption, explicitStore string) (s3ApiServer *S3ApiServer, err error) {
    // ... existing code ...

    // Create traditional IAM
    iam := NewIdentityAccessManagementWithStore(option, explicitStore)

    s3ApiServer = &S3ApiServer{
        option:            option,
        iam:               iam,  // Keep existing for compatibility
        randomClientId:    util.RandomInt32(),
        filerGuard:        security.NewGuard([]string{}, signingKey, expiresAfterSec, readSigningKey, readExpiresAfterSec),
        cb:                NewCircuitBreaker(option),
        credentialManager: iam.credentialManager,
        bucketConfigCache: NewBucketConfigCache(5 * time.Minute),
    }

    // Optional: Wrap with policy-backed IAM for enhanced features
    if option.EnablePolicyEngine {  // Add this config option
        // Option 1: Create and set legacy IAM separately
        policyBackedIAM := NewPolicyBackedIAM()
        policyBackedIAM.SetLegacyIAM(iam)
        
        // Option 2: Create with legacy IAM in one call (convenience method)
        // policyBackedIAM := NewPolicyBackedIAMWithLegacy(iam)
        
        // Load existing identities as policies
        if err := policyBackedIAM.LoadIdentityPolicies(); err != nil {
            glog.Warningf("Failed to load identity policies: %v", err)
        }
        
        // Replace IAM with policy-backed version
        s3ApiServer.iam = policyBackedIAM
    }

    // ... rest of existing code ...
}

Router Integration

// In registerRouter function, replace bucket policy handlers:

// Old handlers (if they exist):
// bucket.Methods(http.MethodGet).HandlerFunc(s3a.GetBucketPolicyHandler).Queries("policy", "")
// bucket.Methods(http.MethodPut).HandlerFunc(s3a.PutBucketPolicyHandler).Queries("policy", "")
// bucket.Methods(http.MethodDelete).HandlerFunc(s3a.DeleteBucketPolicyHandler).Queries("policy", "")

// New handlers with policy engine:
if policyBackedIAM, ok := s3a.iam.(*PolicyBackedIAM); ok {
    // Use policy-backed handlers
    bucket.Methods(http.MethodGet).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(policyBackedIAM.GetBucketPolicyHandler, ACTION_READ)), "GET")).Queries("policy", "")
    bucket.Methods(http.MethodPut).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(policyBackedIAM.PutBucketPolicyHandler, ACTION_WRITE)), "PUT")).Queries("policy", "")
    bucket.Methods(http.MethodDelete).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(policyBackedIAM.DeleteBucketPolicyHandler, ACTION_WRITE)), "DELETE")).Queries("policy", "")
} else {
    // Use existing/fallback handlers
    bucket.Methods(http.MethodGet).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.GetBucketPolicyHandler, ACTION_READ)), "GET")).Queries("policy", "")
    bucket.Methods(http.MethodPut).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.PutBucketPolicyHandler, ACTION_WRITE)), "PUT")).Queries("policy", "")
    bucket.Methods(http.MethodDelete).HandlerFunc(track(s3a.iam.Auth(s3a.cb.Limit(s3a.DeleteBucketPolicyHandler, ACTION_WRITE)), "DELETE")).Queries("policy", "")
}

Configuration Option

Add to S3ApiServerOption:

type S3ApiServerOption struct {
    // ... existing fields ...
    EnablePolicyEngine bool  // Add this field
}

Example Usage

1. Existing Users (No Changes)

Your existing identities.json continues to work:

{
  "identities": [
    {
      "name": "user1",
      "credentials": [{"accessKey": "key1", "secretKey": "secret1"}],
      "actions": ["Read:bucket1/*", "Write:bucket1/uploads/*"]
    }
  ]
}

2. New Users (Enhanced Policies)

Set bucket policies via S3 API:

# Allow public read
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json

# Where policy.json contains:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/*"
    }
  ]
}

3. Advanced Conditions

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::secure-bucket/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "192.168.1.0/24"
        },
        "Bool": {
          "aws:SecureTransport": "true"
        }
      }
    }
  ]
}

Migration Strategy

Phase 1: Enable Policy Engine (Opt-in)

  • Set EnablePolicyEngine: true in server options
  • Existing identities.json automatically converted to policies
  • Add bucket policies as needed

Phase 2: Full Policy Management

  • Use AWS CLI/SDK for policy management
  • Gradually migrate from identities.json to pure IAM policies
  • Take advantage of advanced conditions and features

Testing

# Test existing functionality
go test -v -run TestCanDo

# Test new policy engine
go test -v -run TestPolicyEngine

# Test integration
go test -v -run TestPolicyBackedIAM

The integration is designed to be:

  • Backward compatible - Existing setups work unchanged
  • Opt-in - Enable policy engine only when needed
  • Gradual - Migrate at your own pace
  • AWS compatible - Use standard AWS tools and patterns