mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-18 21:26:56 +00:00
Prefer IAM path when policies exist
This commit is contained in:
@@ -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) {
|
||||
bucketName := "test-bucket-" + randomString(8)
|
||||
|
||||
@@ -509,7 +595,7 @@ func findAvailablePorts(n int) ([]int, error) {
|
||||
}
|
||||
|
||||
// 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
|
||||
// We need 8 unique ports: Master(2), Volume(2), Filer(2), S3(2)
|
||||
ports, err := findAvailablePorts(8)
|
||||
@@ -585,8 +671,7 @@ func startMiniCluster(t *testing.T) (*TestCluster, error) {
|
||||
os.Chdir(testDir)
|
||||
|
||||
// Configure args for mini command
|
||||
os.Args = []string{
|
||||
"weed",
|
||||
baseArgs := []string{
|
||||
"-dir=" + testDir,
|
||||
"-master.port=" + strconv.Itoa(masterPort),
|
||||
"-master.port.grpc=" + strconv.Itoa(masterGrpcPort),
|
||||
@@ -603,6 +688,10 @@ func startMiniCluster(t *testing.T) (*TestCluster, error) {
|
||||
"-master.peers=none", // Faster startup
|
||||
"-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
|
||||
glog.MaxSize = 1024 * 1024
|
||||
@@ -633,6 +722,10 @@ func startMiniCluster(t *testing.T) (*TestCluster, error) {
|
||||
return cluster, nil
|
||||
}
|
||||
|
||||
func startMiniCluster(t *testing.T) (*TestCluster, error) {
|
||||
return startMiniClusterWithExtraArgs(t, nil)
|
||||
}
|
||||
|
||||
// Stop stops the test cluster
|
||||
func (c *TestCluster) Stop() {
|
||||
if c.cancel != nil {
|
||||
|
||||
@@ -46,8 +46,9 @@ func NewS3TablesClient(endpoint, region, accessKey, secretKey string) *S3TablesC
|
||||
|
||||
// Test configuration constants
|
||||
const (
|
||||
testRegion = "us-west-2"
|
||||
testAccessKey = "admin"
|
||||
testSecretKey = "admin"
|
||||
testAccountID = "111122223333"
|
||||
testRegion = "us-west-2"
|
||||
testAccessKey = "admin"
|
||||
testSecretKey = "admin"
|
||||
testAccountID = "111122223333"
|
||||
testIAMSigningKey = "dGVzdC1zaWduaW5nLWtleS1mb3Itc3RzLWludGVncmF0aW9uLXRlc3Rz"
|
||||
)
|
||||
|
||||
@@ -28,11 +28,12 @@ func (h *S3TablesHandler) handleCreateTableBucket(w http.ResponseWriter, r *http
|
||||
|
||||
principal := h.getAccountID(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)
|
||||
tableBucketARN := h.generateTableBucketARN(ownerAccountID, 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 {
|
||||
h.writeError(w, http.StatusForbidden, ErrCodeAccessDenied, "not authorized to create table buckets")
|
||||
return NewAuthError("CreateTableBucket", principal, "not authorized to create table buckets")
|
||||
|
||||
@@ -22,14 +22,17 @@ func (h *S3TablesHandler) SetIAMAuthorizer(authorizer IAMAuthorizer) {
|
||||
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 {
|
||||
return false
|
||||
}
|
||||
if hasSessionToken(r) {
|
||||
return true
|
||||
}
|
||||
return len(identityActions) == 0
|
||||
if len(identityActions) == 0 {
|
||||
return true
|
||||
}
|
||||
return len(identityPolicyNames) > 0
|
||||
}
|
||||
|
||||
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") != ""
|
||||
}
|
||||
|
||||
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 {
|
||||
return false, nil
|
||||
}
|
||||
@@ -67,7 +70,10 @@ func (h *S3TablesHandler) authorizeIAMAction(r *http.Request, action string, res
|
||||
}
|
||||
|
||||
requestContext := buildIAMRequestContext(r, getIdentityClaims(r))
|
||||
policyNames := getIdentityPolicyNames(r)
|
||||
policyNames := identityPolicyNames
|
||||
if len(policyNames) == 0 {
|
||||
policyNames = getIdentityPolicyNames(r)
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for _, resource := range resources {
|
||||
|
||||
Reference in New Issue
Block a user