Skip to content

Conversation

@shirady
Copy link
Contributor

@shirady shirady commented Nov 23, 2025

Describe the Problem

Currently in Containerised NooBaa deployment, when a user is created under an account (and has access keys), they can perform all S3 operations. Since we added the IAM User Policy APIs, we want to change the default so the user must have a policy for any S3 operations.

Explain the Changes

  1. Change authorize_request in s3_rest.js so that the IAM policy check will be after authorize_request_account and before authorize_request_policy.
  2. Check for IAM user policy in case it is not NC deployment.

Issues:

List of GAPs:

  1. The tests are only manual at this point; there is a plan to add automated tests after we have the design change.
  2. I might want to refactor the code so that s3_bucket_policy_utils.js so the terms will be policy in general and can be used for bucket policy and IAM policy.
  3. I might want to move the created function somewhere else (perhaps iam_rest) so the thrown error will be amError.AccessDeniedException instead of S3Error.AccessDenied

Testing Instructions:

  1. Build the images and install NooBaa system on Rancher Desktop (see guide).
    Note: nb is an alias that runs the local operator from build/_output/bin (alias created by devenv).
  2. Wait for the default backing store pod to be in state Ready before starting the tests: kubectl wait --for=condition=available backingstore/noobaa-default-backing-store --timeout=6m -n test1
  3. I'm using port-forward (in a different tab):
  • S3 kubectl port-forward -n test1 service/s3 12443:443
  • IAM kubectl port-forward -n test1 service/iam 14443:443
  1. Create the alias for the admin - first, need to get the credentials: nb status --show-secrets -n test1 and then:
    alias admin-s3='AWS_ACCESS_KEY=<access-key> AWS_SECRET_ACCESS_KEY=<secret-key> aws --no-verify-ssl --endpoint-url https://localhost:12443'
    alias admin-iam='AWS_ACCESS_KEY=<access-key> AWS_SECRET_ACCESS_KEY=<secret-key> aws --no-verify-ssl --endpoint-url https://localhost:14443'
  2. Check the connection to the endpoint:
  • try to list the users (should be an empty list): admin-iam iam list-users; echo $?
  • try to list the buckets (should be first.bucket): admin-iam s3 ls; echo $?
  1. Create a user: admin-iam iam create-user --user-name Robert
    Note: To validate user creation, you can rerun admin-iam iam list-users and expect 1 user in the list
  2. Create access keys: admin-iam iam create-access-keys --user-name Robert
  3. Create the alias for the user (like in step 4 with alias user-1-s3): alias user-1-s3='AWS_ACCESS_KEY=<access-key> AWS_SECRET_ACCESS_KEY=<secret-key> aws --no-verify-ssl --endpoint-url https://localhost:12443'
  4. The case with no bucket policy, no IAM policy - the user would be restricted from S3 operation, for example:
  • The user lists objects in a bucket: user-1-s3 s3 ls s3://first.bucket (should not work - should throw AccessDenied error).
  1. The admin adds an inline policy so the user can list objects in the bucket.
    policy_allow_list_bucket.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "*"
        }
    ]
}
  1. admin-iam iam put-user-policy --user-name Robert --policy-name policy_allow_list_bucket --policy-document file://policy_allow_list_bucketjson
  2. Repeat step 9:
  • The user lists objects in a bucket: user-1-s3 s3 ls s3://first.bucket (should work).

Code changes for testing:

  1. To see the account (of a user) in the cache after changes, src/sdk/object_sdk.js uses cache expiry of 1 millisecond.
const account_cache = new LRUCache({
    name: 'AccountCache',
-    expiry_ms: config.OBJECT_SDK_ACCOUNT_CACHE_EXPIRY_MS,
+   expiry_ms: 1, //SDSD 

Notes:

  • In step 1 - deploying the system, I used --use-standalone-db for simplicity (fewer steps for the system in Ready status).

  • I used the admin account, but for IAM operations, there is no difference between a created account and an admin account. I tested with the admin only because it saves steps (I have the admin account upon system creation and a bucket first.bucket).

  • Doc added/updated

  • Tests added

Summary by CodeRabbit

  • Documentation

    • Clarified that an IAM user policy is required for S3 operations and updated ordering to show the IAM user policy evaluated before the bucket policy; minor wording tweaks.
  • Refactor

    • Authorization checks now run sequentially rather than in parallel, changing control flow and error timing.
    • Tightened conditions under which IAM policy validation may be bypassed for specific deployments and added notes for future IAM handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 23, 2025

Walkthrough

Docs updated to require IAM user policies for S3 operations and reflect a reordering of authorization layers. S3 request authorization now runs sequentially: account → IAM user policy → bucket policy; IAM policy check is skipped only when no iam_policies and nsfs_config_root is set.

Changes

Cohort / File(s) Summary
Documentation
docs/design/IamUserInlinePolicy.md
Reworded to require an IAM user policy for performing S3 operations; reordered "Under The Hood" to show IAM user policy evaluated before bucket policy; minor phrasing edits to match runtime behavior.
S3 Authorization Logic
src/endpoint/s3/s3_rest.js
Replaced parallel authorization checks with sequential awaits (authorize_request_account → authorize_request_iam_policy → authorize_request_policy); changed IAM bypass to only skip when iam_policies.length === 0 AND req.object_sdk.nsfs_config_root is set; added TODO and comment around IAM handling and error messaging.

Sequence Diagram(s)

sequenceDiagram
    participant Client as S3 Client
    participant Handler as s3_rest / authorize_request
    participant Account as authorize_request_account
    participant IAMPolicy as authorize_request_iam_policy
    participant BucketPolicy as authorize_request_policy

    Client->>Handler: HTTP S3 request
    Note right of Handler: Sequential authorization flow
    Handler->>Account: await authorize_request_account(req)
    Account-->>Handler: account auth result
    Handler->>IAMPolicy: await authorize_request_iam_policy(req)
    alt iam_policies empty AND nsfs_config_root set
        IAMPolicy-->>Handler: skipped (no IAM check)
    else
        IAMPolicy-->>Handler: IAM policy decision (allow/deny)
    end
    Handler->>BucketPolicy: await authorize_request_policy(req)
    BucketPolicy-->>Handler: bucket policy decision
    Handler-->>Client: final authorization result (allow/deny)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Pay particular attention to src/endpoint/s3/s3_rest.js for correctness of sequential awaits, error propagation, and the updated IAM bypass condition.
  • Verify docs/design/IamUserInlinePolicy.md accurately matches implemented ordering and wording around when IAM policy is required.

Possibly related PRs

Suggested reviewers

  • aayushchouhan09
  • liranmauda

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: modifying default authorization behavior for IAM users without policies, which is reflected in both the documentation changes and code refactoring in s3_rest.js.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
docs/design/IamUserInlinePolicy.md (1)

7-7: Consider more concise phrasing.

The phrase "in order to" can be simplified to "to" for improved readability.

Apply this diff:

-User must have IAM policy in order to be authorized for S3 operations.
+User must have IAM policy to be authorized for S3 operations.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b54db3 and d7a6151.

📒 Files selected for processing (2)
  • docs/design/IamUserInlinePolicy.md (2 hunks)
  • src/endpoint/s3/s3_rest.js (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-12T04:55:42.193Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_rest.js:258-261
Timestamp: 2025-11-12T04:55:42.193Z
Learning: In the context of S3 REST requests (src/endpoint/s3/s3_rest.js), the account.owner field from req.object_sdk.requesting_account is already a string (account ID) because it comes from RPC serialization where owner._id.toString() is applied in account_server.js. No additional .toString() or ._id extraction is needed when passing account.owner to IAM utility functions.

Applied to files:

  • src/endpoint/s3/s3_rest.js
🪛 LanguageTool
docs/design/IamUserInlinePolicy.md

[style] ~7-~7: Consider a more concise word here.
Context: ...M User Policy User must have IAM policy in order to be authorized for S3 operations. ## Us...

(IN_ORDER_TO_PREMIUM)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: run-jest-unit-tests
  • GitHub Check: run-package-lock-validation
  • GitHub Check: Build Noobaa Image
🔇 Additional comments (4)
docs/design/IamUserInlinePolicy.md (1)

32-33: LGTM!

The reordering of authorization layers correctly reflects the sequential authorization flow implemented in the code changes, where IAM user policy is now checked before bucket policy.

src/endpoint/s3/s3_rest.js (3)

223-227: LGTM! Sequential authorization enforces proper policy precedence.

The change from parallel to sequential execution is appropriate for authorization checks. This ensures IAM user policies are evaluated before bucket policies, which aligns with the documented authorization layers and provides fail-fast behavior when policies deny access.


336-336: TODO noted for future refactoring.

The TODO comment correctly identifies future work to move this function and use IAM-specific error types. This aligns with the known gaps mentioned in the PR objectives.


349-349: Code change verified as correct.

The condition at line 349 properly uses req.object_sdk.nsfs_config_root as the discriminator between NC and containerized deployments. The codebase consistently applies this pattern: nsfs_config_root is truthy in NC/NSFS deployments and falsy in containerized deployments.

The logic correctly implements the intended behavior:

  • NC deployments skip IAM policy validation (returning early when policies are empty)
  • Containerized deployments enforce IAM policies (proceeding to access denial logic when policies are empty)

This change maintains backward compatibility for existing NC deployments while adding proper IAM policy enforcement to containerized deployments.

@shirady shirady force-pushed the iam-user-policy-default-behavior branch from e986557 to c92a0ba Compare November 23, 2025 09:14
@shirady shirady changed the title IAM | IAM Policy as Default Behavior IAM | Change Default Behavior of Users Without IAM User Policy Nov 24, 2025
@shirady shirady force-pushed the iam-user-policy-default-behavior branch from c92a0ba to 13b9f15 Compare November 24, 2025 07:14
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/endpoint/s3/s3_rest.js (1)

336-372: IAM vs NC behavior is correct; consider tightening the “no policies” logging

The IAM check now does what you want:

  • IAM users in containerised mode with no iam_user_policies get AccessDenied here, before bucket policy.
  • NC (nsfs_config_root truthy) still bypasses IAM when iam_policies.length === 0, preserving existing behavior while IAM isn’t supported there.

One small nit: when iam_policies.length === 0 in containerised mode, we still log “user have inline policies but none of them matched the method”, which is misleading. You could make the zero‑policy case explicit without changing semantics, e.g.:

-    const iam_policies = account.iam_user_policies || [];
-    if (iam_policies.length === 0 && req.object_sdk.nsfs_config_root) return; // We do not have IAM policies in NC yet
+    const iam_policies = account.iam_user_policies || [];
+    if (iam_policies.length === 0) {
+        if (req.object_sdk.nsfs_config_root) {
+            // We do not have IAM policies in NC yet
+            return;
+        }
+        dbg.log1('authorize_request_iam_policy: IAM user has no inline policies configured');
+        throw new S3Error(S3Error.AccessDenied);
+    }
@@
-    if (has_allow_permission) return;
-    dbg.log1('authorize_request_iam_policy: user have inline policies but none of them matched the method');
-    throw new S3Error(S3Error.AccessDenied);
+    if (has_allow_permission) return;
+    dbg.log1('authorize_request_iam_policy: user has inline policies but none of them matched the method');
+    throw new S3Error(S3Error.AccessDenied);

This keeps NC semantics intact, keeps the deny behavior for containerised users without policies, and makes logs more accurate.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c92a0ba and 13b9f15.

📒 Files selected for processing (2)
  • docs/design/IamUserInlinePolicy.md (2 hunks)
  • src/endpoint/s3/s3_rest.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/design/IamUserInlinePolicy.md
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-12T04:55:42.193Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_rest.js:258-261
Timestamp: 2025-11-12T04:55:42.193Z
Learning: In the context of S3 REST requests (src/endpoint/s3/s3_rest.js), the account.owner field from req.object_sdk.requesting_account is already a string (account ID) because it comes from RPC serialization where owner._id.toString() is applied in account_server.js. No additional .toString() or ._id extraction is needed when passing account.owner to IAM utility functions.

Applied to files:

  • src/endpoint/s3/s3_rest.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: run-package-lock-validation
  • GitHub Check: Build Noobaa Image
  • GitHub Check: run-jest-unit-tests
🔇 Additional comments (1)
src/endpoint/s3/s3_rest.js (1)

221-227: Layered authorization flow (account → IAM → bucket) looks consistent with the new semantics

The new sequencing (load_requesting_accountauthorize_request_accountauthorize_request_iam_policyauthorize_request_policy) correctly enforces IAM user checks before bucket policy, while still allowing root/anon paths to behave as before and keeping bucket policy as a final ownership/explicit‑DENY layer. This matches the PR’s goal for containerised deployments without introducing obvious regressions.

@shirady shirady force-pushed the iam-user-policy-default-behavior branch from 13b9f15 to 39ed7aa Compare November 24, 2025 07:41
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/endpoint/s3/s3_rest.js (4)

218-225: Sequential authorization changes request flow.

The authorization flow was changed from parallel to sequential execution. While this ensures the correct evaluation order (account → IAM user policy → bucket policy), it means every request now waits for the IAM policy check to complete before bucket policy evaluation begins.

Consider monitoring the latency impact of this change in production. If the IAM policy check becomes a bottleneck, you may want to optimize policy evaluation or implement caching strategies.


346-346: Verify the early-exit condition is explicit and clear.

The condition req.object_sdk.nsfs_config_root should be explicitly checked for boolean truthiness to avoid potential issues with falsy values like empty strings.

Apply this diff to make the check more explicit:

-    if (iam_policies.length === 0 && req.object_sdk.nsfs_config_root) return; // We do not have IAM policies in NC yet
+    if (iam_policies.length === 0 && Boolean(req.object_sdk.nsfs_config_root)) return; // We do not have IAM policies in NC yet

Alternatively, if nsfs_config_root is always expected to be a string path or undefined, consider:

-    if (iam_policies.length === 0 && req.object_sdk.nsfs_config_root) return; // We do not have IAM policies in NC yet
+    if (iam_policies.length === 0 && req.object_sdk.nsfs_config_root !== undefined) return; // We do not have IAM policies in NC yet

366-368: Improve error context for IAM policy denial.

The error message on line 367 and the thrown error on line 368 don't clearly distinguish between two different denial scenarios:

  1. User has NO IAM policies (in containerized deployments)
  2. User has IAM policies but none allow the requested action

This makes debugging and user feedback less clear.

Apply this diff to provide clearer error messages:

-    if (has_allow_permission) return;
-    dbg.log1('authorize_request_iam_policy: user have inline policies but none of them matched the method');
-    throw new S3Error(S3Error.AccessDenied);
+    if (has_allow_permission) return;
+    
+    if (iam_policies.length === 0) {
+        dbg.log1('authorize_request_iam_policy: IAM user must have at least one user policy in containerized deployments');
+    } else {
+        dbg.log1('authorize_request_iam_policy: user has inline policies but none allow the requested action');
+    }
+    throw new S3Error(S3Error.AccessDenied);

333-334: Address the TODO comment before finalizing this feature.

The TODO mentions moving this function and improving error messages. Given this is a significant security change affecting user experience, addressing these improvements should be prioritized.

The suggested improvements in the TODO are valid:

  1. Moving to iam_rest would allow returning amError.AccessDeniedException instead of S3Error.AccessDenied, providing more appropriate IAM-specific error details.
  2. Enhanced error messages would help users understand why their requests are denied.

Do you want me to open an issue to track these improvements, or would you prefer to address them in this PR?

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13b9f15 and 39ed7aa.

📒 Files selected for processing (2)
  • docs/design/IamUserInlinePolicy.md (2 hunks)
  • src/endpoint/s3/s3_rest.js (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-12T04:55:42.193Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-core PR: 9277
File: src/endpoint/s3/s3_rest.js:258-261
Timestamp: 2025-11-12T04:55:42.193Z
Learning: In the context of S3 REST requests (src/endpoint/s3/s3_rest.js), the account.owner field from req.object_sdk.requesting_account is already a string (account ID) because it comes from RPC serialization where owner._id.toString() is applied in account_server.js. No additional .toString() or ._id extraction is needed when passing account.owner to IAM utility functions.

Applied to files:

  • src/endpoint/s3/s3_rest.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Noobaa Image
  • GitHub Check: run-package-lock-validation
  • GitHub Check: run-jest-unit-tests
🔇 Additional comments (1)
docs/design/IamUserInlinePolicy.md (1)

28-36: LGTM!

The authorization layer description accurately reflects the sequential flow implemented in the code changes.

@shirady shirady merged commit 5a51122 into noobaa:master Nov 24, 2025
18 checks passed
@shirady shirady self-assigned this Nov 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants