Skip to main content

S3 Object Lock

Explanation of Object Lock

Object lock makes objects immutable, which means that they can't be encrypted and held for ransom by bad actors.

There are two modes of object lock:

  1. Governance Mode
  2. Compliance Mode

In governance mode, objects can be deleted by users who have the s3:BypassGovernanceRetention permission.

In compliance mode, objects cannot be deleted before a specified "retention date". No user can delete objects that have been locked in this way.

General Format of Commands

The general format of commands that set object lock is as follows:

aws --profile=ceph --endpoint-url <URL> s3api put-object-legal-hold \
--bucket <bucket_name> \
--key <object> \
--version-id <version> \
--legal-hold Status=OFF

Governance Mode

In this procedure, we will create a bucket that has Object Lock enabled in governance mode, and then attempt to delete it. This procedure will demonstrate the security of Object Lock in governance mode by demonstrating the impossibility of deleting the bucket unless the user has the s3:BypassGovernanceRetention permission.

  1. Create a bucket that has Object Lock enabled:

    $ aws --endpoint-url=http://xx2.xx.xxx.x s3api create-bucket --bucket test-bucket --object-lock-enabled-for-bucket
  2. Add the object-lock configuration (this can be "governance" or "compliance", but "governance" is used here):

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api put-object-lock-configuration --bucket test-bucket --object-lock-configuration '{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"GOVERNANCE","Days":90}}}'
  3. Add an object to the bucket:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api put-object --bucket test-bucket --body some-object.png --key some-object.png
    {
    "ETag": "\"xxx\"",
    "VersionId": "xxx"
    }
  4. Now that the object has been created, we will attempt to delete it:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api delete-object --bucket test-bucket --key some-object.png
    {
    "DeleteMarker": true,
    "VersionId": "xxx"
    }
    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api delete-object --bucket test-bucket --key some-object.png --version-id xxx
    An error occurred (AccessDenied) when calling the DeleteObject operation: forbidden by object lock
    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api get-object-retention --bucket test-bucket --key some-object.png --version-id xxx
    {
    "Retention": {
    "Mode": "GOVERNANCE",
    "RetainUntilDate": "2026-12-31T23:59:00.000000+00:00"
    }
    }

    This demonstrates that the object cannot be deleted.

  5. Now we demonstrate that the user test is able to bypass governance mode:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api delete-object --bucket test-bucket --key some-object.png --version-id xxx --bypass-governance-retention
    {
    "VersionId": "xxx"
    }
    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api list-object-versions --bucket test-bucket {
    "DeleteMarkers": [
    {
    "Owner": {
    "DisplayName": "test",
    "ID": "test"
    },
    "Key": "some-object.png",
    "VersionId": "xxx",
    "IsLatest": true,
    "LastModified": "2023-01-01T12:00:00.000000+00:00"
    }
    ]
    }

    This demonstrates that the user test is able to bypass the restrictions set in governance mode.

  6. Now we will demonstrate that a user cannot bypass the restrictions set in governance mode. Apply the following policy, which removes the s3:BypassGovernanceRetention permission from all users including the test user:

    {
    "Version": "2026-01-01",
    "Statement": [
    {
    "Effect": "Deny",
    "Principal": {
    "AWS": [
    "*"
    ]
    },
    "Action": "s3:BypassGovernanceRetention",
    "Resource": "*"
    }
    ]
    }
  7. Attempt to use the s3api delete-object command on the bucket called test-bucket:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api delete-object --bucket test-bucket --key some-object.png --version-id a0a0aa0a0aaaaaaa.0aaa0a0.0aaaa0 --bypass-governance-retention
    An error occurred (AccessDenied) when calling the DeleteObject operation: forbidden by object lock

    The bucket has been protected from deletion because s3:BypassGovernanceRetention has been removed.

Compliance Mode

In this procedure, we will create a bucket that has Object Lock enabled in compliance mode, then attempt to delete it, then attempt to change the retention period that has been set. This procedure will demonstrate the security of Object Lock in compliance mode by demonstrating the impossibility of deleting the bucket and then demonstrating the impossibility of reducing the length of the retention period. Note that it is possible to extend the length of the compliance period.

  1. Create a bucket that has Object Lock enabled:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api create-bucket \
    --bucket test-bucket --object-lock-enabled-for-bucket
  2. Add the object-lock configuration (this can be "governance" or "compliance", but "compliance" is used here). In this example the Object Lock is set for ninety (90) days:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api put-object-lock-configuration --bucket test-bucket \
    --object-lock-configuration \
    '{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":90}}}'
  3. Add an object to the bucket:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api put-object --bucket test-bucket \
    --body some-object.png --key some-object.png
    {
    "ETag": "\"xxx\"",
    "VersionId": "xxx"
    }
  4. Run the following command with the get-object-retention command to ensure that the object's retention mode is set to COMPLIANCE:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api get-object-retention --bucket test-bucket \
    --key some-object.png
    {
    "Retention": {
    "Mode": "COMPLIANCE",
    "RetainUntilDate": "2026-12-31T23:59:59.000000+00:00"
    }
    }
  5. Attempt to delete the object. In this example, we attempt to delete the object without specifying the version ID:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api delete-object --bucket test-bucket --key some-object.png
    {
    "DeleteMarker": true,
    "VersionId": "xxx"
    }
  6. Use the list-object-versions command to see whether the delete marker has been set:

    $ aws --endpoint-url=http://172.31.117.5 s3api list-object-versions --bucket test-bucket
    {
    "Versions": [
    {
    "ETag": "\"xxx\"",
    "Size": 10000000,
    "StorageClass": "STANDARD",
    "Key": "some-object.png",
    "VersionId": "xxx",
    "IsLatest": false,
    "LastModified": "2026-06-01T12:00:00.000000+00:00",
    "Owner": {
    "DisplayName": "test",
    "ID": "test"
    }
    }
    ],
    "DeleteMarkers": [
    {
    "Owner": {
    "DisplayName": "test",
    "ID": "test"
    },
    "Key": "some-object.png",
    "VersionId": "OsXsNSWZe2oLisQ4.kEeDo1xjwklui0",
    "IsLatest": true,
    "LastModified": "2026-06-01T12:00:00.000000+00:00"
    }
    ]
    }
  7. Attempt to delete the object again by using the delete-object command and specifying the version ID:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api delete-object \
    --bucket test-bucket --key some-object.png \
    --version-id aaaaaa0aaa00a0aasaaa0aa0aaa0aaa


    An error occurred (AccessDenied) when calling the DeleteObject operation:
    forbidden by object lock
  8. The deletion of the object is forbidden by the object lock that was put in place earlier in this procedure. Now we will attempt to remove that object lock (to demonstrate that it cannot be removed under these conditions):

    $ aws --endpoint-url=http://xxx.xx.xxx.x \
    s3api put-object-retention --bucket test-bucket --key some-object.png \
    --version-id xxx \
    --retention '{ "Mode": "GOVERNANCE", "RetainUntilDate": "2026-12-31T23:59:59.000000+00:00" }'

    An error occurred (AccessDenied) when calling the PutObjectRetention operation: can't change retention
    mode from COMPLIANCE to GOVERNANCE
  9. Now we will attempt to reduce the duration of the retention period (to demonstrate that it cannot be reduced under these conditions):

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api put-object-retention \
    --bucket test-bucket --key some-object.png \
    --version-id aaaaaa0aaa00a0aasaaa0aa0aaa0aaa \
    --retention '{ "Mode": "COMPLIANCE", "RetainUntilDate": "2026-02-01T00:00:00" }'
    An error occurred (AccessDenied) when calling the PutObjectRetention
    operation: proposed retain-until date shortens an existing retention
    period and governance bypass check failed

    This demonstrates that compliance mode, once set on an object, makes it impossible to delete that object until the retention date has passed.

  10. Although the retention period cannot be reduced in compliance mode, it can be extended:

    aws s3api get-object-retention --bucket lock-3 --key hello.txt { "Retention": { "Mode": "COMPLIANCE", "RetainUntilDate": "2026-02-18T17:31:44.520197+00:00" } }

```bash
aws s3api put-object-retention --bucket lock-3 --key hello.txt --version-id u8V-t5ZFAEUh7Tvobo88aSCgwG-pn4R --retention '{ "Mode": "COMPLIANCE", "RetainUntilDate": "2026-03-01"}'
aws s3api get-object-retention --bucket lock-3 --key hello.txt --version-id u8V-t5ZFAEUh7Tvobo88aSCgwG-pn4R { "Retention": { "Mode": "COMPLIANCE", "RetainUntilDate": "2026-03-01T00:00:00+00:00" } }
aws s3api put-object-retention --bucket lock-3 --key hello.txt --version-id u8V-t5ZFAEUh7Tvobo88aSCgwG-pn4R --retention '{ "Mode": "COMPLIANCE", "RetainUntilDate": "2026-02-20"}'
An error occurred (AccessDenied) when calling the PutObjectRetention operation: proposed retain-until date shortens an existing retention period and governance bypass check failed

Legal hold is a feature of S3 Object Lock that prevents an object version from being overwritten or deleted. Unlike governance mode and compliance mode, which use retention periods, a legal hold has no associated retention period and remains in effect until it is explicitly removed.

Legal hold has only two settings: ON and OFF. This setting will override the deletion behavior of GOVERNANCE.

Legal holds are useful in situations where objects must be preserved indefinitely for legal or regulatory reasons, such as during litigation or investigations. A legal hold can be applied to an object at any time, regardless of whether the object already has a retention period set.

In this procedure, we will create a bucket that has Object Lock enabled, apply a legal hold to an object, attempt to delete the object, and then remove the legal hold. This procedure will demonstrate how legal holds protect objects from deletion and how they differ from retention-based modes.

  1. Create a bucket that has Object Lock enabled:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api create-bucket \
    --bucket test-bucket --object-lock-enabled-for-bucket
  2. Add an object to the bucket:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api put-object \
    --bucket test-bucket --body some-object.png --key some-object.png
    {
    "ETag": "\"xxx\"",
    "VersionId": "xxx"
    }
  3. Apply a legal hold to the object:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api put-object-legal-hold \
    --bucket test-bucket --key some-object.png \
    --legal-hold Status=ON
  4. Verify that the legal hold has been applied:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api get-object-legal-hold \
    --bucket test-bucket --key some-object.png
    {
    "LegalHold": {
    "Status": "ON"
    }
    }
  5. Attempt to delete the object:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api delete-object \
    --bucket test-bucket --key some-object.png \
    --version-id xxx

    An error occurred (AccessDenied) when calling the DeleteObject operation:
    The specified object version is protected by a Legal Hold

    This demonstrates that the object cannot be deleted while a legal hold is in place.

  6. Remove the legal hold from the object:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api put-object-legal-hold \
    --bucket test-bucket --key some-object.png \
    --legal-hold Status=OFF
  7. Verify that the legal hold has been removed:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api get-object-legal-hold \
    --bucket test-bucket --key some-object.png
    {
    "LegalHold": {
    "Status": "OFF"
    }
    }
  8. Now that the legal hold has been removed, attempt to delete the object again:

    $ aws --endpoint-url=http://xxx.xx.xxx.x s3api delete-object \
    --bucket test-bucket --key some-object.png \
    --version-id xxx
    {
    "VersionId": "xxx"
    }

    This demonstrates that once the legal hold is removed, the object can be deleted (assuming no other retention mode is in effect).

To apply or remove a legal hold from objects, users must have the correct permissions. Here is the list of those permissions:

s3:GetBucketObjectLockConfiguration s3:PutObjectLegalHold s3:BypassGovernanceRetention s3:GetBucketObjectLockConfiguration s3:GetObjectLegalHold s3:GetObjectRetention s3:PutBucketObjectLockConfiguration s3:PutObjectLegalHold s3:PutObjectRetention

  • Legal holds work independently of retention modes. An object can have both a legal hold and a retention period applied simultaneously.
  • Legal holds apply to specific object versions. If no version is specified, the legal hold is applied to the latest version.
  • Unlike governance mode, legal holds cannot be bypassed with special permissions. They must be explicitly removed.
  • Legal holds do not have expiration dates. They remain in effect until manually removed.
  • S3 Batch Operations can be used to apply or remove legal holds on multiple objects at once.
  • See also the AWS CLI Command Reference for "put-object-legal-hold"

Discussion