or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

access-control-security.mdbucket-management.mdindex.mdlifecycle-storage.mdnotifications-events.md
tile.json

access-control-security.mddocs/

Access Control and Security

The AWS CDK S3 library provides comprehensive security features to protect your S3 buckets and objects. This includes IAM-based access control with convenient grant methods, public access controls, encryption options, SSL enforcement, and bucket policies for fine-grained permissions management.

Core Imports

import { BucketEncryption, BlockPublicAccess, BucketPolicy } from '@aws-cdk/aws-s3';
import * as iam from '@aws-cdk/aws-iam';
import * as kms from '@aws-cdk/aws-kms';

IAM Grant Methods

All bucket instances provide convenient methods for granting IAM permissions to AWS principals:

interface IBucket {
  grantRead(identity: IGrantable, objectsKeyPattern?: any): Grant;
  grantWrite(identity: IGrantable, objectsKeyPattern?: any): Grant;
  grantReadWrite(identity: IGrantable, objectsKeyPattern?: any): Grant;
  grantPut(identity: IGrantable, objectsKeyPattern?: any): Grant;
  grantPutAcl(identity: IGrantable, objectsKeyPattern?: string): Grant;
  grantDelete(identity: IGrantable, objectsKeyPattern?: any): Grant;
  grantPublicAccess(keyPrefix?: string, ...allowedActions: string[]): Grant;
}

Basic Grant Operations

const bucket = new s3.Bucket(this, 'MyBucket');
const role = new iam.Role(this, 'MyRole', {
  assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com')
});

// Grant read access to all objects
bucket.grantRead(role);

// Grant write access to all objects  
bucket.grantWrite(role);

// Grant full read/write access
bucket.grantReadWrite(role);

// Grant delete permissions
bucket.grantDelete(role);

Scoped Permissions

Grant permissions to specific object patterns using key prefixes:

// Grant read access only to 'public/' folder
bucket.grantRead(role, 'public/*');

// Grant write access to user-specific folder
bucket.grantWrite(role, `users/${userId}/*`);

// Grant read/write to specific file types
bucket.grantReadWrite(role, '*.json');

// Multiple patterns can be granted separately
bucket.grantRead(role, 'images/*');
bucket.grantRead(role, 'documents/*.pdf');

Advanced Grant Methods

// Grant object upload permissions (PUT operations)
bucket.grantPut(myLambda, 'uploads/*');

// Grant ACL modification permissions (requires explicit call as of CDK 1.85.0+)
bucket.grantPutAcl(role, 'shared/*');

// Grant public read access with optional restrictions
const grant = bucket.grantPublicAccess('public/*', 's3:GetObject');
grant.resourceStatement!.addCondition('IpAddress', { 
  'aws:SourceIp': '203.0.113.0/24' 
});

Encryption Key Permissions

When buckets use KMS encryption, grant methods automatically include necessary key permissions:

const encryptionKey = new kms.Key(this, 'MyKey');
const bucket = new s3.Bucket(this, 'MyBucket', {
  encryption: s3.BucketEncryption.KMS,
  encryptionKey: encryptionKey
});

// Automatically grants both S3 and KMS permissions
bucket.grantReadWrite(role); // Includes kms:Decrypt and kms:Encrypt permissions

Encryption Options

S3 buckets support multiple encryption methods for data protection:

enum BucketEncryption {
  UNENCRYPTED = 'NONE',           // No encryption
  S3_MANAGED = 'S3MANAGED',       // Server-side encryption with S3-managed keys (SSE-S3)
  KMS_MANAGED = 'MANAGED',        // Server-side encryption with KMS-managed keys (SSE-KMS)
  KMS = 'KMS'                     // Server-side encryption with customer-managed KMS keys
}

Encryption Configuration

// S3-managed encryption (SSE-S3)
const s3ManagedBucket = new s3.Bucket(this, 'S3ManagedBucket', {
  encryption: s3.BucketEncryption.S3_MANAGED
});

// KMS-managed encryption with AWS managed key
const kmsManagedBucket = new s3.Bucket(this, 'KmsManagedBucket', {
  encryption: s3.BucketEncryption.KMS_MANAGED
});

// Customer-managed KMS key encryption
const customerKey = new kms.Key(this, 'CustomerKey');
const customerManagedBucket = new s3.Bucket(this, 'CustomerManagedBucket', {
  encryption: s3.BucketEncryption.KMS,
  encryptionKey: customerKey
});

// Enable S3 Bucket Key for cost optimization with KMS
const bucketKeyBucket = new s3.Bucket(this, 'BucketKeyBucket', {
  encryption: s3.BucketEncryption.KMS,
  bucketKeyEnabled: true  // Reduces KMS request costs
});

Encryption Best Practices

// Recommended secure configuration
const secureBucket = new s3.Bucket(this, 'SecureBucket', {
  encryption: s3.BucketEncryption.KMS,
  bucketKeyEnabled: true,
  enforceSSL: true,  // Require HTTPS for all requests
  versioned: true,   // Enable versioning for data protection
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL
});

// Access the automatically created encryption key
if (secureBucket.encryptionKey) {
  secureBucket.encryptionKey.addAlias('alias/my-bucket-key');
}

Public Access Controls

Control public access to buckets and objects using Block Public Access settings:

class BlockPublicAccess {
  static readonly BLOCK_ALL: BlockPublicAccess;
  static readonly BLOCK_ACLS: BlockPublicAccess;
  
  readonly blockPublicAcls?: boolean;
  readonly blockPublicPolicy?: boolean;
  readonly ignorePublicAcls?: boolean;
  readonly restrictPublicBuckets?: boolean;
  
  constructor(options: BlockPublicAccessOptions);
}

interface BlockPublicAccessOptions {
  blockPublicAcls?: boolean;        // Block public ACLs on bucket and objects
  blockPublicPolicy?: boolean;      // Block public bucket policies
  ignorePublicAcls?: boolean;       // Ignore public ACLs on bucket and objects
  restrictPublicBuckets?: boolean;  // Restrict public bucket policies
}

Public Access Configuration

// Block all public access (recommended for most use cases)
const privateeBucket = new s3.Bucket(this, 'PrivateBucket', {
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL
});

// Block only ACL-based public access
const aclBlockedBucket = new s3.Bucket(this, 'AclBlockedBucket', {
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ACLS
});

// Custom public access controls
const customBlockedBucket = new s3.Bucket(this, 'CustomBlockedBucket', {
  blockPublicAccess: new s3.BlockPublicAccess({
    blockPublicPolicy: true,    // Block public policies
    ignorePublicAcls: true      // Ignore public ACLs
  })
});

// Allow public read access (use carefully)
const publicBucket = new s3.Bucket(this, 'PublicBucket', {
  publicReadAccess: true,  // Grants public GetObject permissions
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ACLS  // Still block ACL-based access
});

Public Access Validation

When blockPublicPolicy is enabled, attempting to grant public access will throw an error:

const blockedBucket = new s3.Bucket(this, 'BlockedBucket', {
  blockPublicAccess: new s3.BlockPublicAccess({ blockPublicPolicy: true })
});

// This will throw an error
try {
  blockedBucket.grantPublicAccess();
} catch (error) {
  console.log("Cannot grant public access when 'blockPublicPolicy' is enabled");
}

Bucket Policies

Create custom IAM policies for fine-grained access control:

class BucketPolicy extends Resource {
  constructor(scope: Construct, id: string, props: BucketPolicyProps);
  readonly document: PolicyDocument;
  applyRemovalPolicy(removalPolicy: RemovalPolicy): void;
}

interface BucketPolicyProps {
  readonly bucket: IBucket;
  readonly removalPolicy?: RemovalPolicy;
}

Creating Bucket Policies

const bucket = new s3.Bucket(this, 'MyBucket');

// Create a bucket policy
const bucketPolicy = new s3.BucketPolicy(this, 'MyBucketPolicy', {
  bucket: bucket
});

// Add policy statements
bucketPolicy.document.addStatements(
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    principals: [new iam.AccountPrincipal('123456789012')],
    actions: ['s3:GetObject'],
    resources: [bucket.arnForObjects('shared/*')]
  }),
  
  new iam.PolicyStatement({
    effect: iam.Effect.DENY,
    principals: [new iam.AnyPrincipal()],
    actions: ['s3:*'],
    resources: [bucket.bucketArn, bucket.arnForObjects('*')],
    conditions: {
      Bool: { 'aws:SecureTransport': 'false' }  // Require HTTPS
    }
  })
);

Alternative: Using addToResourcePolicy

Most commonly, use the bucket's addToResourcePolicy method instead of creating a BucketPolicy directly:

bucket.addToResourcePolicy(new iam.PolicyStatement({
  effect: iam.Effect.ALLOW,
  principals: [new iam.ServicePrincipal('cloudtrail.amazonaws.com')],
  actions: ['s3:PutObject'],
  resources: [bucket.arnForObjects('AWSLogs/*')],
  conditions: {
    StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' }
  }
}));

SSL/HTTPS Enforcement

Enforce secure transport for all bucket operations:

const secureBucket = new s3.Bucket(this, 'SecureBucket', {
  enforceSSL: true  // Automatically creates a policy denying non-HTTPS requests
});

// Manual SSL enforcement via bucket policy
bucket.addToResourcePolicy(new iam.PolicyStatement({
  effect: iam.Effect.DENY,
  principals: [new iam.AnyPrincipal()],
  actions: ['s3:*'],
  resources: [bucket.bucketArn, bucket.arnForObjects('*')],
  conditions: {
    Bool: { 'aws:SecureTransport': 'false' }
  }
}));

Security Best Practices Example

import * as s3 from '@aws-cdk/aws-s3';
import * as kms from '@aws-cdk/aws-kms';
import * as iam from '@aws-cdk/aws-iam';

export class SecureS3Stack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string) {
    super(scope, id);

    // Create a secure bucket with comprehensive security controls
    const secureEncryptionKey = new kms.Key(this, 'SecureKey', {
      description: 'S3 bucket encryption key',
      enableKeyRotation: true,  // Enable automatic key rotation
    });

    const secureBucket = new s3.Bucket(this, 'SecureBucket', {
      // Encryption settings
      encryption: s3.BucketEncryption.KMS,
      encryptionKey: secureEncryptionKey,
      bucketKeyEnabled: true,
      
      // Access controls
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      enforceSSL: true,
      
      // Data protection
      versioned: true,
      lifecycleRules: [{
        id: 'DeleteOldVersions',
        noncurrentVersionExpiration: cdk.Duration.days(90),
      }],
      
      // Logging and monitoring
      serverAccessLogsPrefix: 'access-logs/',
      
      // Deletion protection
      removalPolicy: cdk.RemovalPolicy.RETAIN
    });

    // Create application role with minimal permissions
    const appRole = new iam.Role(this, 'AppRole', {  
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      description: 'Role for application S3 access'
    });

    // Grant scoped permissions
    secureBucket.grantRead(appRole, 'public/*');
    secureBucket.grantReadWrite(appRole, 'app-data/*');
    
    // Add custom policy for specific requirements
    secureBucket.addToResourcePolicy(new iam.PolicyStatement({
      effect: iam.Effect.DENY,
      principals: [new iam.AnyPrincipal()],
      actions: ['s3:DeleteBucket'],
      resources: [secureBucket.bucketArn],
      conditions: {
        StringNotEquals: {
          'aws:PrincipalServiceName': 'cloudformation.amazonaws.com'
        }
      }
    }));

    // Output key ARN for reference
    new cdk.CfnOutput(this, 'EncryptionKeyArn', {
      value: secureEncryptionKey.keyArn,
      description: 'ARN of the S3 bucket encryption key'
    });
  }
}