Browse Source

Prefer IAM path when policies exist

pull/8388/head
Chris Lu 2 days ago
parent
commit
30b36f149e
  1. 99
      test/s3tables/table-buckets/s3tables_integration_test.go
  2. 9
      test/s3tables/table-buckets/setup.go
  3. 5
      weed/s3api/s3tables/handler_bucket_create.go
  4. 14
      weed/s3api/s3tables/iam.go

99
test/s3tables/table-buckets/s3tables_integration_test.go

@ -70,6 +70,92 @@ func TestS3TablesIntegration(t *testing.T) {
}) })
} }
func TestS3TablesCreateBucketIAMPolicy(t *testing.T) {
if testing.Short() {
t.Skip("Skipping IAM integration test in short mode")
}
t.Setenv("AWS_ACCESS_KEY_ID", "env-admin")
t.Setenv("AWS_SECRET_ACCESS_KEY", "env-secret")
allowedBucket := "tables-allowed"
deniedBucket := "tables-denied"
iamConfigDir := t.TempDir()
iamConfigPath := filepath.Join(iamConfigDir, "iam_config.json")
iamConfig := fmt.Sprintf(`{
"sts": {
"tokenDuration": "1h",
"maxSessionLength": "12h",
"issuer": "seaweedfs-sts",
"signingKey": "%s"
},
"accounts": [
{
"id": "%s",
"displayName": "tables-integration"
}
],
"identities": [
{
"name": "admin",
"credentials": [
{
"accessKey": "%s",
"secretKey": "%s"
}
],
"account": {
"id": "%s",
"displayName": "tables-integration"
},
"policyNames": ["S3TablesBucketPolicy"]
}
],
"policy": {
"defaultEffect": "Deny",
"storeType": "memory"
},
"policies": [
{
"name": "S3TablesBucketPolicy",
"document": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3tables:CreateTableBucket"],
"Resource": [
"arn:aws:s3tables:%s:%s:bucket/%s",
"arn:aws:s3:::%s"
]
}
]
}
}
]
}`, testIAMSigningKey, testAccountID, testAccessKey, testSecretKey, testAccountID, testRegion, testAccountID, allowedBucket, allowedBucket)
require.NoError(t, os.WriteFile(iamConfigPath, []byte(iamConfig), 0644))
cluster, err := startMiniClusterWithExtraArgs(t, []string{
"-s3.config=" + iamConfigPath,
"-s3.iam.config=" + iamConfigPath,
})
require.NoError(t, err, "failed to start cluster with IAM config")
defer cluster.Stop()
client := NewS3TablesClient(cluster.s3Endpoint, testRegion, testAccessKey, testSecretKey)
_, err = client.CreateTableBucket(deniedBucket, nil)
require.Error(t, err, "denied bucket creation should fail")
assert.Contains(t, err.Error(), "AccessDenied")
allowedResp, err := client.CreateTableBucket(allowedBucket, nil)
require.NoError(t, err, "allowed bucket creation should succeed")
defer func() {
_ = client.DeleteTableBucket(allowedResp.ARN)
}()
}
func testTableBucketLifecycle(t *testing.T, client *S3TablesClient) { func testTableBucketLifecycle(t *testing.T, client *S3TablesClient) {
bucketName := "test-bucket-" + randomString(8) bucketName := "test-bucket-" + randomString(8)
@ -509,7 +595,7 @@ func findAvailablePorts(n int) ([]int, error) {
} }
// startMiniCluster starts a weed mini instance directly without exec // startMiniCluster starts a weed mini instance directly without exec
func startMiniCluster(t *testing.T) (*TestCluster, error) {
func startMiniClusterWithExtraArgs(t *testing.T, extraArgs []string) (*TestCluster, error) {
// Find available ports // Find available ports
// We need 8 unique ports: Master(2), Volume(2), Filer(2), S3(2) // We need 8 unique ports: Master(2), Volume(2), Filer(2), S3(2)
ports, err := findAvailablePorts(8) ports, err := findAvailablePorts(8)
@ -585,8 +671,7 @@ func startMiniCluster(t *testing.T) (*TestCluster, error) {
os.Chdir(testDir) os.Chdir(testDir)
// Configure args for mini command // Configure args for mini command
os.Args = []string{
"weed",
baseArgs := []string{
"-dir=" + testDir, "-dir=" + testDir,
"-master.port=" + strconv.Itoa(masterPort), "-master.port=" + strconv.Itoa(masterPort),
"-master.port.grpc=" + strconv.Itoa(masterGrpcPort), "-master.port.grpc=" + strconv.Itoa(masterGrpcPort),
@ -603,6 +688,10 @@ func startMiniCluster(t *testing.T) (*TestCluster, error) {
"-master.peers=none", // Faster startup "-master.peers=none", // Faster startup
"-s3.iam.readOnly=false", // Enable IAM write operations for tests "-s3.iam.readOnly=false", // Enable IAM write operations for tests
} }
if len(extraArgs) > 0 {
baseArgs = append(baseArgs, extraArgs...)
}
os.Args = append([]string{"weed"}, baseArgs...)
// Suppress most logging during tests // Suppress most logging during tests
glog.MaxSize = 1024 * 1024 glog.MaxSize = 1024 * 1024
@ -633,6 +722,10 @@ func startMiniCluster(t *testing.T) (*TestCluster, error) {
return cluster, nil return cluster, nil
} }
func startMiniCluster(t *testing.T) (*TestCluster, error) {
return startMiniClusterWithExtraArgs(t, nil)
}
// Stop stops the test cluster // Stop stops the test cluster
func (c *TestCluster) Stop() { func (c *TestCluster) Stop() {
if c.cancel != nil { if c.cancel != nil {

9
test/s3tables/table-buckets/setup.go

@ -46,8 +46,9 @@ func NewS3TablesClient(endpoint, region, accessKey, secretKey string) *S3TablesC
// Test configuration constants // Test configuration constants
const ( const (
testRegion = "us-west-2"
testAccessKey = "admin"
testSecretKey = "admin"
testAccountID = "111122223333"
testRegion = "us-west-2"
testAccessKey = "admin"
testSecretKey = "admin"
testAccountID = "111122223333"
testIAMSigningKey = "dGVzdC1zaWduaW5nLWtleS1mb3Itc3RzLWludGVncmF0aW9uLXRlc3Rz"
) )

5
weed/s3api/s3tables/handler_bucket_create.go

@ -28,11 +28,12 @@ func (h *S3TablesHandler) handleCreateTableBucket(w http.ResponseWriter, r *http
principal := h.getAccountID(r) principal := h.getAccountID(r)
identityActions := getIdentityActions(r) identityActions := getIdentityActions(r)
if h.shouldUseIAM(r, identityActions) && !h.defaultAllow {
identityPolicyNames := getIdentityPolicyNames(r)
if h.shouldUseIAM(r, identityActions, identityPolicyNames) && !h.defaultAllow {
ownerAccountID := h.getAccountID(r) ownerAccountID := h.getAccountID(r)
tableBucketARN := h.generateTableBucketARN(ownerAccountID, req.Name) tableBucketARN := h.generateTableBucketARN(ownerAccountID, req.Name)
s3BucketARN := fmt.Sprintf("arn:aws:s3:::%s", req.Name) s3BucketARN := fmt.Sprintf("arn:aws:s3:::%s", req.Name)
allowed, err := h.authorizeIAMAction(r, "s3tables:CreateTableBucket", tableBucketARN, s3BucketARN)
allowed, err := h.authorizeIAMAction(r, identityPolicyNames, "s3tables:CreateTableBucket", tableBucketARN, s3BucketARN)
if err != nil || !allowed { if err != nil || !allowed {
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to create table buckets") h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to create table buckets")
return NewAuthError("CreateTableBucket", principal, "not authorized to create table buckets") return NewAuthError("CreateTableBucket", principal, "not authorized to create table buckets")

14
weed/s3api/s3tables/iam.go

@ -22,14 +22,17 @@ func (h *S3TablesHandler) SetIAMAuthorizer(authorizer IAMAuthorizer) {
h.iamAuthorizer = authorizer h.iamAuthorizer = authorizer
} }
func (h *S3TablesHandler) shouldUseIAM(r *http.Request, identityActions []string) bool {
func (h *S3TablesHandler) shouldUseIAM(r *http.Request, identityActions, identityPolicyNames []string) bool {
if h.iamAuthorizer == nil || r == nil { if h.iamAuthorizer == nil || r == nil {
return false return false
} }
if hasSessionToken(r) { if hasSessionToken(r) {
return true return true
} }
return len(identityActions) == 0
if len(identityActions) == 0 {
return true
}
return len(identityPolicyNames) > 0
} }
func hasSessionToken(r *http.Request) bool { func hasSessionToken(r *http.Request) bool {
@ -42,7 +45,7 @@ func hasSessionToken(r *http.Request) bool {
return r.URL.Query().Get("X-Amz-Security-Token") != "" return r.URL.Query().Get("X-Amz-Security-Token") != ""
} }
func (h *S3TablesHandler) authorizeIAMAction(r *http.Request, action string, resources ...string) (bool, error) {
func (h *S3TablesHandler) authorizeIAMAction(r *http.Request, identityPolicyNames []string, action string, resources ...string) (bool, error) {
if h.iamAuthorizer == nil { if h.iamAuthorizer == nil {
return false, nil return false, nil
} }
@ -67,7 +70,10 @@ func (h *S3TablesHandler) authorizeIAMAction(r *http.Request, action string, res
} }
requestContext := buildIAMRequestContext(r, getIdentityClaims(r)) requestContext := buildIAMRequestContext(r, getIdentityClaims(r))
policyNames := getIdentityPolicyNames(r)
policyNames := identityPolicyNames
if len(policyNames) == 0 {
policyNames = getIdentityPolicyNames(r)
}
var lastErr error var lastErr error
for _, resource := range resources { for _, resource := range resources {

Loading…
Cancel
Save