HDDS-15945. [OEP] Support S3 WORM (Object Lock) - #11053
chungen0126 wants to merge 21 commits into
Conversation
devmadhuu
left a comment
There was a problem hiding this comment.
Thanks @chungen0126 for putting up the design. Curiously had a look and compared against AWS S3 specs. Got few doubts and points.
|
|
||
| > _**Note**: | ||
| > AWS S3 supports enabling Object Lock and configuring RetentionConfig directly during bucket creation. | ||
| > In Apache Ozone's current design, these settings must be configured via dedicated API calls after the bucket has been created. |
There was a problem hiding this comment.
In contrast to AWS S3, how do we present this to compliance auditors? Or should there be a "one-way" flag that, once set, cannot be cleared?
There was a problem hiding this comment.
Thanks for pointing that out.
- Enabling at Creation: We can definitely support enabling Object Lock directly at bucket creation time (e.g., via the S3 CreateBucket header/parameters) to eliminate any compliance window. I updated the doc.
- One-way Flag: Exactly, objectLockEnabled is designed as a one-way flag—once enabled on a bucket, it cannot be cleared or disabled.
|
|
||
| - Definition: Configures a fixed retention duration (specified in days or years) for an object and applies a specific retention mode. | ||
| - Retention Modes: | ||
| - Compliance Mode: The strictest protection tier. Once applied, no user (including root/admin) can remove the lock, shorten the duration, or overwrite the object before the retention period expires. |
There was a problem hiding this comment.
Is it really true for Ozone. In AWS S3, it is being made sure to be immutable at all levels or atleast audited or blocked including admin/operators users, but in Ozone, this may not be true, currently design talks about a happy path using validateAndUpdateCache when hit on normal S3/ozone APIs. In Ozone there are other ways where someone can directly edit rocksDb or delete a key meta for a locked key. Another way, where ozone repair command also allows to restore OM's DB directory from a ratis snapshot taken before the lock was applied.
Probably the design should talk about behavior when such actions can happen in Ozone , that what is trusted boundary and upto what extent the data is protected because otherwise this design may not suffice the complete compliance audit implementation.
Can think of below:
- Whether
ozone repairsubcommands should refuse to operate on Raft indexes that carry lock state changes without an explicit unsafe flag. - Whether locked keys should be detected on OM startup via cross-check between RocksDB state and a tamper-evident external audit log — so that a manual DB-copy recovery that silently removes a lock can at least be detected on the next start.
- an operator can run
skip-ratis-transactionat the Raft index of a SetRetention(compliance) entry across all 3 OMs while stopped, then restart, and the audit trail of the lock is gone (though RocksDB may still hold applied state
There was a problem hiding this comment.
Thanks for pointing this out!
I've added a Trusted Boundary section to clarify that any operations requiring root access bypass the application-level API and are outside the trusted boundary.
|
|
||
| message RetentionConfig { | ||
| optional RetentionMode retentionMode = 23; | ||
| optional uint64 retainUntilDate = 24; |
There was a problem hiding this comment.
Is it possible to move the OM leader's clock forward: The WORM check in validateAndUpdateCache will compare retainUntilDate (stored) against "now". "Now" is whatever the OM leader believes the time is.
|
|
||
| #### Bucket Table | ||
|
|
||
| Two new fields: objectLockEnabled & defaultRetention. |
There was a problem hiding this comment.
Since this object Locking is from S3, what interface via s3 api will be used to configure or manage? or its directly via ozone cli?
There was a problem hiding this comment.
Object locking can be configured and managed via both the Ozone API and the S3 API.
- Ozone API: It uses OzoneBucket#setRetentionConfig. If no default retention is set, the rule field within retention will simply be null.
- S3 API: It is managed through PutBucketObjectLockConfiguration, which basically follows the standard S3 specifications. You can refer to the official AWS S3 documentation for more details.
|
|
||
| ### Ranger Access Control | ||
|
|
||
| - Legal Hold: Introduces three new Access Types—GET_LEGAL_HOLD, PUT_LEGAL_HOLD, and CLEAR_LEGAL_HOLD—and adds them to accessTypeRestrictions for Keys. |
There was a problem hiding this comment.
If we introduce this feature in Ozone for validation via above api, Having same at ranger do have any specific advantage ?
There was a problem hiding this comment.
how ranger and ozone role will combine in validation?
|
|
||
| #### Impact on Delete Object Flow | ||
|
|
||
| All delete operations must perform WORM validation during the `validateAndUpdateCache` phase to preserve linearizability and prevent accidental deletion of protected data. |
There was a problem hiding this comment.
do impact for bucket removal or volume removal or directory removal where as per soft delete, it moves to deleted directory table, but generally validation is not done to deep child ?
There was a problem hiding this comment.
This can be divided into two parts:
- Bucket or volume removal:
Currently, a bucket cannot be deleted if it still contains keys. Therefore, this is not impacted. - Directory removal:
Regarding the non-goal, the current design does not support FSO or legacy buckets, so it will not touch upon directory removal.
|
|
||
| ## Security | ||
|
|
||
| Centralized access control via Ranger ensures all Object Lock operations (such as Retention Policy configuration and Legal Hold management) are enforced under strict access permissions. Dedicated Access Types (BYPASS_GOVERNANCE, PUT_LEGAL_HOLD, etc.) enforce the Principle of Least Privilege, preventing unauthorized tampering or removal of locks and enhancing data immutability. |
There was a problem hiding this comment.
Like ranger can clear / update policy, and super admin role to update / delete bucket or file policy ?
There was a problem hiding this comment.
Thanks for pointing this out!
I've added a Trusted Boundary section to clarify that any operations requiring root access bypass the application-level API and are outside the trusted boundary.
| message BucketInfo { | ||
| // ... existing fields | ||
| required bool objectLockEnabled = 24 [default = false]; | ||
| optional RetentionConfig defaultRetention = 25; |
There was a problem hiding this comment.
Thanks @chungen0126 for working on this design.
It looks like bucket level retention configuration is different than object level. It support days and years, not the retainUntilDate.
https://docs.aws.amazon.com/cli/latest/reference/s3api/put-object-lock-configuration.html
|
|
||
| * **`GET_LEGAL_HOLD`**: Allows querying the current Legal Hold status of an object (maps to the `GetObjectLegalHold` API). | ||
| * **`PUT_LEGAL_HOLD`**: Grants permission to apply a Legal Hold to an object (maps to `PutObjectLegalHold` with status ON). Once applied, the object is locked indefinitely, blocking all overwrite and delete operations. | ||
| * **`CLEAR_LEGAL_HOLD`**: Grants permission to remove a Legal Hold from an object (maps to `PutObjectLegalHold` with status OFF). Because this clears immutability protection, it is treated as a high-privilege action restricted only to authorized users. |
There was a problem hiding this comment.
CLEAR_LEGAL_HOLD can be covered by PUT_LEGAL_HOLD, since AWS S3 doesn't have CLEAR_LEGAL_HOLD permission too, so let's avoid introduce a new extra permission.
There was a problem hiding this comment.
hi all - you won't need to create new access types for this. From the STS project, the concept of actions are created in Ranger, which map exactly to the AWS actions without the s3: prefix. From the AWS documentation (https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html), there are 7 new actions:
s3:BypassGovernanceRetention
s3:GetBucketObjectLockConfiguration
s3:GetObjectLegalHold
s3:GetObjectRetention
s3:PutBucketObjectLockConfiguration
s3:PutObjectLegalHold
s3:PutObjectRetention
so in Ranger these would be:
BypassGovernanceRetention
GetBucketObjectLockConfiguration
GetObjectLegalHold
GetObjectRetention
PutBucketObjectLockConfiguration
PutObjectLegalHold
PutObjectRetention
For example, if you want the role to have access PutObjectLegalHold, you would grant PutObjectLegalHold action and write permission (i.e. access type) in Ranger. Or you could grant Put* and all permission and get access to all the Put actions.
There was a problem hiding this comment.
The mappings in Ranger for actions to permissions/access types are found here: https://github.com/apache/ranger/blob/master/security-admin/src/main/webapp/react-webapp/src/utils/actionRequirements/ozone.json.
Also, please note that when STS is merged to master, the IamSessionPolicyResolver would need to get updated with the new actions as well as S3GActionIamMapper.
There was a problem hiding this comment.
Also, like @ChenSammi says, I believe you need to separate the authorization from the value being set. For example, the same PutObjectLegalHold authorization can be used with values ON or OFF in the API (see https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObjectLegalHold.html)
There was a problem hiding this comment.
Also, to use the actions in Ranger, ensure to set the Ranger feature flag to true: https://ozone.apache.org/docs/next/administrator-guide/operations/s3/sts#ranger-feature-flag-action-matches-policy-condition
| > * **Object Lock Immutability & Future Versioning Binding**: | ||
| > - **Current State (No Versioning)**: Once Object Lock is enabled on a bucket (`objectLockEnabled = true`), it cannot be disabled. | ||
| > - **Future Evolution (With Versioning)**: When S3 Versioning is supported, Object Lock and Versioning will be tightly coupled following AWS S3 semantics: once Object Lock is enabled, Versioning cannot be suspended or disabled. | ||
| > * **Bucket Creation Semantics**: |
There was a problem hiding this comment.
There is one more S3 behavior, versioning is enabled automatically when Object Lock is enabled.
| Standard data mutating APIs will intercept the Object Lock state and return the `403 Access Denied` (or specifically `WORMProtectionException`) if a modification is attempted on a locked object: | ||
| * `PutObject` / `CopyObject` (Overwrites will be rejected) | ||
| * `DeleteObject` / `DeleteObjects` (Deletions will be rejected) | ||
| * `CreateMultipartUpload` / `CompleteMultipartUpload` / `UploadPart` (Uploads will be rejected if the object is locked) |
There was a problem hiding this comment.
Reject upload in CreateMultipartUpload is good enough for the MPU, CompleteMultipartUpload and UploadPart can be untouched.
There was a problem hiding this comment.
In a distributed development environment, it is possible for your application to initiate several updates on the same object at the same time. Your application might initiate several multipart uploads using the same object key. For each of these uploads, your application can then upload parts and send a complete upload request to Amazon S3 to create the object. When the buckets have S3 Versioning enabled, completing a multipart upload always creates a new version. When you initiate multiple multipart uploads that use the same object key in a versioning-enabled bucket, the current version of the object is determined by which upload started most recently (createdDate).
AWS S3 explicitly allows multiple concurrent multipart uploads for the same key.
If we only reject requests at CreateMultipartUpload, an MPU initiated before the lock is applied could still be completed after the lock becomes active. This would overwrite the protected object and break the WORM guarantee. Therefore, we still need to validate the WORM state during CompleteMultipartUpload to ensure strict immutability.
|
|
||
| ## Compatibility | ||
|
|
||
| Because this design alters the schemas of Bucket Table and Key Table, OM (Ozone Manager) version leveling will be introduced to maintain forward and backward compatibility across rolling upgrades. |
There was a problem hiding this comment.
I think the major reason is we have added new OM APIs for the feature, so we need a new OM version to tell client whether these APIs can be called or not. And table wise, since all new fields are optional fields, they are naturally backward compatible.
|
|
||
| ### Ranger Access Control | ||
|
|
||
| Ozone Object Lock enforces a dual-gate mechanism combining **Ranger authorization checks** and **underlying WORM state validation**: |
There was a problem hiding this comment.
Do we have an JIRA open in Ranger project to track all the changes required for this feature?
There was a problem hiding this comment.
@fmorg-git , would you mind sharing some knowledge with @chungen0126 about how to expose new permissions for Ranger, and what are the major tasks needed for Ranger integration, including UI changes?
| * **Compliance Mode**: Serves as the strictest compliance tier. Until the retention period expires, **no role or Ranger permission can bypass or overwrite the lock**, including cluster administrators. | ||
| * **Governance Mode and `BYPASS_GOVERNANCE`**: | ||
| * **Mechanism**: Governance Mode allows authorized users to overwrite, delete, or alter the retention duration of a locked object before its expiration date. | ||
| * **Authorization Binding**: Introduces the **`BYPASS_GOVERNANCE`** Access Type, configured under `accessTypeRestrictions` at the **Volume level**. |
There was a problem hiding this comment.
Why BYPASS_GOVERNANCE is configured at volume level?
There was a problem hiding this comment.
I believe BypassGovernanceRetention should be checked at the key level.
There was a problem hiding this comment.
Thanks @ChenSammi, @devmadhuu @sumitagrawl, and @fmorg-git for the review! I’ve updated the design doc based on your feedback—please take another look.
Regarding the Ranger integration part, I'm still relatively new to it and actively catching up, so I would really appreciate it if @fmorg-git could help take a look. I'll continue refining the Ranger-related details in upcoming updates.
|
@chungen0126 , just want to highlight to be in compliance with AWS S3 WORM, that AWS has recently added variable retention with event holds to S3 Object Lock, so may be we can think about adding EVENT_TRIGGERED_RETENTION in our existing enum for extended support. Enterprise S3 semantics are increasingly about governance workflows, not just object durability. Reference : AWS S3 doc |
Thanks @devmadhuu for the reminder. I add a sub-section in Terminology to introduce the trigger workflow. |
|
|
||
| Ozone Object Lock enforces a dual-gate protection mechanism combining **Ranger policy-based authorization** and **underlying OM WORM state validation**: | ||
|
|
||
| * **Standard Data Operations (Put / Delete)**: Even if a principal has valid Ranger `WRITE` or `DELETE` access, any attempt to mutate, overwrite, or delete an object under an active Legal Hold or an unexpired Retention period is immediately rejected with `403 Access Denied` (`WORMProtectionException`). |
There was a problem hiding this comment.
nit - I believe Put requires CREATE and WRITE access.
| | **`PutObjectRetention`** | Key | `WRITE` | Set or extend object retention mode and duration. | | ||
| | **`GetObjectLegalHold`** | Key | `READ` | Query the current Legal Hold status (`ON` or `OFF`). | | ||
| | **`PutObjectLegalHold`** | Key | `WRITE` | Toggle the Legal Hold state (`ON` or `OFF`). | | ||
| | **`BypassGovernanceRetention`** | Key | `WRITE` / `DELETE` | Privileged entitlement to bypass retention in Governance Mode. | |
There was a problem hiding this comment.
quick clarification - does it mean BypassGovernanceRetention requires WRITE and DELETE together, similar to how PutObject requires WRITE and CREATE together? If so, may want to say and instead of /
|
|
||
| #### 3. Value-Level Authorization & Ranger Condition Evaluator | ||
|
|
||
| While API invocation is controlled by the S3 Action (e.g., granting `PutObjectLegalHold`), real-world governance often requires **role segregation based on request values** (e.g., Compliance Officers can toggle `ON`, but only external Auditors can toggle `OFF`; or preventing operators from setting `COMPLIANCE` mode). |
There was a problem hiding this comment.
curious, what is the reason for this additional complexity? AWS doesn't work like this. FYI @ChenSammi
There was a problem hiding this comment.
Thanks for the feedback! Actually, AWS IAM does support this kind of value-level condition matching for S3.
According to the AWS IAM Condition Operators documentation, S3 provides condition keys that allow restricting specific actions based on request values.
The reason I included this additional complexity is based on use cases like the one described in this AWS re:Post article: Denying removal of Amazon S3 Object Lock Legal Hold. The article demonstrates how administrators separate the permissions for turning a legal hold ON versus OFF by leveraging the StringEquals condition operator. As mentioned in the quote:
As legal hold has no expiration date, users may wish to use this mode to apply an indefinite lock on objects they wish to protect from accidental or malicious deletion. In this scenario, it may be desirable to restrict permissions to remove legal hold from objects. You can do this with a condition key in the Condition element of an IAM policy, specifically "StringEquals": "s3:object-lock-legal-hold": "OFF" applied to the action "PutObjectLegalHold".
That being said, I am completely open to discussing this. If we feel that PutObjectLegalHold (without distinguishing between ON/OFF payloads) is enough for Ozone at this stage, we can definitely drop this custom condition evaluator to keep the initial implementation simple.
Let me know what @fmorg-git and @ChenSammi think!
There was a problem hiding this comment.
thanks for the references @chungen0126 - wasn't aware of it. I implemented Object Lock in the past and hundreds of thousands of customers never needed/asked for it, so I was not aware (also I noticed the AWS article was posted after the implementation). My gut feeling is the complexity required might be overkill compared to how many people might use it (if any). I also note that we currently don't support Deny in STS session policy nor any condition key other than s3:prefix. In any case, I will defer to @ChenSammi to for her thoughts if it is needed for Ozone.
| - `GOVERNANCE` -> Allow only if caller has `BypassGovernanceRetention` permission AND header `x-amz-bypass-governance-retention: true` is present; otherwise reject. | ||
| - If unlocked or bypass authorized, proceed with deletion and quota reclaim. | ||
| - **Batch Delete (`DeleteObjects`)**: | ||
| - In `S3BatchDeleteRequest` / `MultiDeleteEndpoint`: evaluate WORM status per key. |
There was a problem hiding this comment.
These two classes don't exist in master. The path is BucketEndpoint.multiDelete -> OzoneBucket.deleteKeys -> OMKeysDeleteRequest. Checking in S3G also misses KeyLifecycleService, which submits DeleteKeysRequest directly into Ratis and never goes through S3G. Could the check go in OMKeysDeleteRequest.validateAndUpdateCache?
jojochuang
left a comment
There was a problem hiding this comment.
Summary
Doc-only OEP for S3 Object Lock on OBS buckets (Ranger + STS action matcher, single-version v1). CI looks good. Approve — scope, two-phase OM enforcement (preExecute + validateAndUpdateCache), and the trusted-boundary section are strong and actionable.
OEP vs AWS S3 Object Lock (v1)
- Bucket scope: OBS only (no FSO/legacy). AWS applies to versioned S3 buckets generally.
- Versioning: AWS requires versioning; locks are per object version. This OEP v1 is single-version — locks apply to the current key; multi-version lock is Phase 6.
- Overwrites: AWS typically creates a new version; the locked version stays. OEP rejects Put/Copy/MPU on a locked key (stricter without versioning).
- Deletes: AWS is version-aware (403 on protected version; delete markers in some paths). OEP blocks delete on protected keys in v1 (no delete-marker model).
- Enable lock on bucket: Both support create-time and existing-bucket config; AWS uses
x-amz-bucket-object-lock-tokenonPutObjectLockConfiguration— please document that token flow explicitly in the OEP (enable-on-existing-bucket is AWS-aligned). - Modes / bypass: Governance + Compliance + Legal Hold align with AWS;
BypassGovernanceRetention+x-amz-bypass-governance-retention: true; no bypass for Compliance or Legal Hold on delete. - Auth: Lock admin APIs assume Ranger +
enableActionMatcherInPoliciesCondition, not native Ozone ACLs. - Compliance story: Infra bypass (RocksDB, repair, DN files, clock, Ranger admin) is appropriate for self-managed Ozone vs AWS-managed S3.
Main client-visible deltas in v1: no versioning, overwrite blocking, and Ranger dependency — worth a short callout in Summary or Compatibility for operators/SDK authors.
Follow-ups (track on HDDS-15945)
- Add CreateBucket Object Lock and PutObject / MPU
x-amz-object-lock-*headers to the “Supported S3 APIs” section (Phase 4 already covers behavior). - Align Key table narrative with
RetentionConfig/ effective RetainUntilDate (Table Changes still mentionsretentionDate). - Use
optionalproto fields consistently with the Compatibility section (required bool objectLockEnabledin Phase 1 is contradictory for rolling upgrade). - Document Ranger repo coordination (ozone.json, condition evaluators) and non-Ranger clusters (no lock admin path in v1).
- Fix plan wording (five phases vs Phase 6); minor Java sketch (
booleannotbool).
Feature checklist (requested product scope)
Per-bucket Object Lock, per-object retention, Legal Hold, compliance enforcement, and “admin cannot delete” (Compliance / legal hold via API path) are covered by this design. AWS API parity is mostly there for the six lock REST actions + bypass; gaps are mainly CreateBucket, versionId, and token for existing buckets.
Future: FSO buckets and Object LockWe may need to support FILE_SYSTEM_OPTIMIZED (FSO) buckets eventually. The current OEP correctly scopes v1 to OBS, but it would help to add a short “Future: FSO” note so readers do not assume OBS handlers generalize for free. FSO is not natively compatible with the same WORM story as flat OBS keys. AWS Object Lock is S3-object/version oriented; FSO adds a file/directory split, path-based identity, rename, recursive/batch delete, and background purge ( Design decisions to make
Engineering pointers (when FSO is in scope)
Suggest a brief Non-goal today / Future work subsection in the OEP pointing here so OBS v1 can ship without implying FSO is a small delta. |
Observability / displaying WORM stateThe OEP covers querying lock configuration through the standard S3 Object Lock Get APIs ( For seeing lock state on normal object reads, AWS documents
The OEP does not yet call out enriching Head/Get with those headers. That is the main S3 parity gap for “display WORM state without extra GET Operator tooling: The doc does not discuss Suggest a short “Observability” subsection:
|
What changes were proposed in this pull request?
This is the OEP doc for S3 WORM (Object Lock) feature.
What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-15945
How was this patch tested?
No tests.