S3 Object Lock for Data Protection

Implement S3 Object Lock for WORM storage to meet compliance requirements and protect against accidental or malicious deletion.

S3 Object Lock provides WORM (Write Once Read Many) protection for objects, essential for regulatory compliance and ransomware protection.

Object Lock Modes

Governance Mode

Allows users with special permissions to delete or modify:

import boto3

s3 = boto3.client('s3')

def upload_with_governance_lock(bucket, key, body, retention_days):
    from datetime import datetime, timedelta
    
    retain_until = datetime.now() + timedelta(days=retention_days)
    
    s3.put_object(
        Bucket=bucket,
        Key=key,
        Body=body,
        ObjectLockMode='GOVERNANCE',
        ObjectLockRetainUntilDate=retain_until
    )

Compliance Mode

No one can delete or modify, not even root:

def upload_with_compliance_lock(bucket, key, body, retention_days):
    from datetime import datetime, timedelta
    
    retain_until = datetime.now() + timedelta(days=retention_days)
    
    s3.put_object(
        Bucket=bucket,
        Key=key,
        Body=body,
        ObjectLockMode='COMPLIANCE',
        ObjectLockRetainUntilDate=retain_until
    )

Bucket Configuration

Enable Object Lock

ComplianceBucket:
  Type: AWS::S3::Bucket
  Properties:
    BucketName: compliance-data-bucket
    ObjectLockEnabled: true
    ObjectLockConfiguration:
      ObjectLockEnabled: Enabled
      Rule:
        DefaultRetention:
          Mode: COMPLIANCE
          Years: 7
    VersioningConfiguration:
      Status: Enabled
    PublicAccessBlockConfiguration:
      BlockPublicAcls: true
      BlockPublicPolicy: true
      IgnorePublicAcls: true
      RestrictPublicBuckets: true

Terraform Configuration

resource "aws_s3_bucket" "compliance" {
  bucket = "compliance-data-bucket"
  
  object_lock_enabled = true
}

resource "aws_s3_bucket_object_lock_configuration" "compliance" {
  bucket = aws_s3_bucket.compliance.id

  rule {
    default_retention {
      mode = "COMPLIANCE"
      years = 7
    }
  }
}

resource "aws_s3_bucket_versioning" "compliance" {
  bucket = aws_s3_bucket.compliance.id
  
  versioning_configuration {
    status = "Enabled"
  }
}

Legal Holds

Apply Legal Hold

def apply_legal_hold(bucket, key, version_id=None):
    params = {
        'Bucket': bucket,
        'Key': key,
        'LegalHold': {'Status': 'ON'}
    }
    
    if version_id:
        params['VersionId'] = version_id
    
    s3.put_object_legal_hold(**params)
    
    print(f"Legal hold applied to {key}")

def remove_legal_hold(bucket, key, version_id=None):
    params = {
        'Bucket': bucket,
        'Key': key,
        'LegalHold': {'Status': 'OFF'}
    }
    
    if version_id:
        params['VersionId'] = version_id
    
    s3.put_object_legal_hold(**params)

Bulk Legal Hold

def apply_legal_hold_prefix(bucket, prefix):
    paginator = s3.get_paginator('list_object_versions')
    
    for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
        for version in page.get('Versions', []):
            apply_legal_hold(
                bucket,
                version['Key'],
                version['VersionId']
            )

Compliance Validation

Check Object Lock Status

def get_object_lock_status(bucket, key, version_id=None):
    params = {'Bucket': bucket, 'Key': key}
    if version_id:
        params['VersionId'] = version_id
    
    # Get retention
    try:
        retention = s3.get_object_retention(**params)
        retention_info = {
            'mode': retention['Retention']['Mode'],
            'until': retention['Retention']['RetainUntilDate']
        }
    except s3.exceptions.ClientError:
        retention_info = None
    
    # Get legal hold
    try:
        legal_hold = s3.get_object_legal_hold(**params)
        legal_hold_status = legal_hold['LegalHold']['Status']
    except s3.exceptions.ClientError:
        legal_hold_status = None
    
    return {
        'retention': retention_info,
        'legal_hold': legal_hold_status
    }

Audit Compliance

def audit_bucket_compliance(bucket):
    results = {
        'total_objects': 0,
        'locked_objects': 0,
        'legal_holds': 0,
        'non_compliant': []
    }
    
    paginator = s3.get_paginator('list_object_versions')
    
    for page in paginator.paginate(Bucket=bucket):
        for version in page.get('Versions', []):
            results['total_objects'] += 1
            
            status = get_object_lock_status(
                bucket,
                version['Key'],
                version['VersionId']
            )
            
            if status['retention']:
                results['locked_objects'] += 1
            else:
                results['non_compliant'].append(version['Key'])
            
            if status['legal_hold'] == 'ON':
                results['legal_holds'] += 1
    
    return results

Bypass Governance Mode

Users with s3:BypassGovernanceRetention permission:

def delete_governance_locked_object(bucket, key, version_id):
    s3.delete_object(
        Bucket=bucket,
        Key=key,
        VersionId=version_id,
        BypassGovernanceRetention=True
    )

IAM Policy for Bypass

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowGovernanceBypass",
      "Effect": "Allow",
      "Action": [
        "s3:BypassGovernanceRetention"
      ],
      "Resource": "arn:aws:s3:::compliance-bucket/*"
    }
  ]
}

Working with Warqline

We are a cloud engineering consultancy and an official AWS and Google Cloud partner. If you are running this in production and want a second pair of eyes, we scope work in a free 45-minute technical call: you describe what you are running and what worries you, and we tell you what we would look at first.

Talk to an engineer

Conclusion

S3 Object Lock provides essential data protection for compliance and ransomware defense. Use Compliance mode for regulatory requirements and Governance mode for flexible protection with administrative override capabilities.