aws-cis-controls-assessment 1.1.3__py3-none-any.whl → 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. aws_cis_assessment/__init__.py +4 -4
  2. aws_cis_assessment/config/rules/cis_controls_ig1.yaml +365 -2
  3. aws_cis_assessment/controls/ig1/control_access_analyzer.py +198 -0
  4. aws_cis_assessment/controls/ig1/control_access_asset_mgmt.py +360 -0
  5. aws_cis_assessment/controls/ig1/control_access_control.py +323 -0
  6. aws_cis_assessment/controls/ig1/control_backup_security.py +579 -0
  7. aws_cis_assessment/controls/ig1/control_cloudfront_logging.py +215 -0
  8. aws_cis_assessment/controls/ig1/control_configuration_mgmt.py +407 -0
  9. aws_cis_assessment/controls/ig1/control_data_classification.py +255 -0
  10. aws_cis_assessment/controls/ig1/control_dynamodb_encryption.py +279 -0
  11. aws_cis_assessment/controls/ig1/control_ebs_encryption.py +177 -0
  12. aws_cis_assessment/controls/ig1/control_efs_encryption.py +243 -0
  13. aws_cis_assessment/controls/ig1/control_elb_logging.py +195 -0
  14. aws_cis_assessment/controls/ig1/control_guardduty.py +156 -0
  15. aws_cis_assessment/controls/ig1/control_inspector.py +184 -0
  16. aws_cis_assessment/controls/ig1/control_inventory.py +511 -0
  17. aws_cis_assessment/controls/ig1/control_macie.py +165 -0
  18. aws_cis_assessment/controls/ig1/control_messaging_encryption.py +419 -0
  19. aws_cis_assessment/controls/ig1/control_mfa.py +485 -0
  20. aws_cis_assessment/controls/ig1/control_network_security.py +194 -619
  21. aws_cis_assessment/controls/ig1/control_patch_management.py +626 -0
  22. aws_cis_assessment/controls/ig1/control_rds_encryption.py +228 -0
  23. aws_cis_assessment/controls/ig1/control_s3_encryption.py +383 -0
  24. aws_cis_assessment/controls/ig1/control_tls_ssl.py +556 -0
  25. aws_cis_assessment/controls/ig1/control_version_mgmt.py +329 -0
  26. aws_cis_assessment/controls/ig1/control_vpc_flow_logs.py +205 -0
  27. aws_cis_assessment/controls/ig1/control_waf_logging.py +226 -0
  28. aws_cis_assessment/core/models.py +20 -1
  29. aws_cis_assessment/core/scoring_engine.py +98 -1
  30. aws_cis_assessment/reporters/base_reporter.py +31 -1
  31. aws_cis_assessment/reporters/html_reporter.py +172 -11
  32. aws_cis_controls_assessment-1.2.0.dist-info/METADATA +320 -0
  33. {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/RECORD +39 -15
  34. docs/developer-guide.md +204 -5
  35. docs/user-guide.md +137 -4
  36. aws_cis_controls_assessment-1.1.3.dist-info/METADATA +0 -404
  37. {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/WHEEL +0 -0
  38. {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/entry_points.txt +0 -0
  39. {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/licenses/LICENSE +0 -0
  40. {aws_cis_controls_assessment-1.1.3.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,228 @@
1
+ """
2
+ CIS Control 3.11 - RDS Storage Encryption
3
+ Ensures RDS database instances have storage encryption enabled for data at rest protection.
4
+ """
5
+
6
+ import logging
7
+ from typing import List, Dict, Any
8
+ from botocore.exceptions import ClientError
9
+
10
+ from aws_cis_assessment.controls.base_control import BaseConfigRuleAssessment
11
+ from aws_cis_assessment.core.models import ComplianceResult, ComplianceStatus
12
+ from aws_cis_assessment.core.aws_client_factory import AWSClientFactory
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class RDSStorageEncryptedAssessment(BaseConfigRuleAssessment):
18
+ """
19
+ CIS Control 3.11 - Encrypt Sensitive Data at Rest
20
+ AWS Config Rule: rds-storage-encrypted
21
+
22
+ Ensures RDS database instances have storage encryption enabled.
23
+ Encrypted storage protects data at rest, including automated backups,
24
+ read replicas, and snapshots.
25
+ """
26
+
27
+ def __init__(self):
28
+ super().__init__(
29
+ rule_name="rds-storage-encrypted",
30
+ control_id="3.11",
31
+ resource_types=["AWS::RDS::DBInstance"]
32
+ )
33
+
34
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
35
+ """Get all RDS database instances in the region."""
36
+ if resource_type != "AWS::RDS::DBInstance":
37
+ return []
38
+
39
+ try:
40
+ rds_client = aws_factory.get_client('rds', region)
41
+
42
+ db_instances = []
43
+ paginator = rds_client.get_paginator('describe_db_instances')
44
+
45
+ for page in paginator.paginate():
46
+ for db_instance in page.get('DBInstances', []):
47
+ db_instance_id = db_instance.get('DBInstanceIdentifier', '')
48
+ storage_encrypted = db_instance.get('StorageEncrypted', False)
49
+ kms_key_id = db_instance.get('KmsKeyId', '')
50
+ engine = db_instance.get('Engine', '')
51
+ engine_version = db_instance.get('EngineVersion', '')
52
+ db_instance_status = db_instance.get('DBInstanceStatus', '')
53
+
54
+ db_instances.append({
55
+ 'DBInstanceIdentifier': db_instance_id,
56
+ 'StorageEncrypted': storage_encrypted,
57
+ 'KmsKeyId': kms_key_id,
58
+ 'Engine': engine,
59
+ 'EngineVersion': engine_version,
60
+ 'DBInstanceStatus': db_instance_status,
61
+ 'DBInstanceArn': db_instance.get('DBInstanceArn', ''),
62
+ 'MultiAZ': db_instance.get('MultiAZ', False)
63
+ })
64
+
65
+ logger.debug(f"Found {len(db_instances)} RDS instances in {region}")
66
+ return db_instances
67
+
68
+ except ClientError as e:
69
+ error_code = e.response.get('Error', {}).get('Code', '')
70
+ if error_code == 'AccessDenied':
71
+ logger.warning(f"Access denied to list RDS instances in {region}")
72
+ else:
73
+ logger.error(f"Error retrieving RDS instances from {region}: {e}")
74
+ return []
75
+ except Exception as e:
76
+ logger.error(f"Unexpected error retrieving RDS instances from {region}: {e}")
77
+ return []
78
+
79
+ def _evaluate_resource_compliance(
80
+ self,
81
+ resource: Dict[str, Any],
82
+ aws_factory: AWSClientFactory,
83
+ region: str
84
+ ) -> ComplianceResult:
85
+ """Evaluate if RDS instance has storage encryption enabled."""
86
+ db_instance_id = resource.get('DBInstanceIdentifier', 'unknown')
87
+ storage_encrypted = resource.get('StorageEncrypted', False)
88
+ kms_key_id = resource.get('KmsKeyId', '')
89
+ engine = resource.get('Engine', '')
90
+ db_status = resource.get('DBInstanceStatus', '')
91
+
92
+ # Check if storage is encrypted
93
+ is_compliant = storage_encrypted
94
+
95
+ if is_compliant:
96
+ if kms_key_id:
97
+ evaluation_reason = (
98
+ f"RDS instance '{db_instance_id}' ({engine}) has storage encryption enabled "
99
+ f"with KMS key: {kms_key_id}"
100
+ )
101
+ else:
102
+ evaluation_reason = (
103
+ f"RDS instance '{db_instance_id}' ({engine}) has storage encryption enabled "
104
+ f"with AWS managed key"
105
+ )
106
+ compliance_status = ComplianceStatus.COMPLIANT
107
+ else:
108
+ evaluation_reason = (
109
+ f"RDS instance '{db_instance_id}' ({engine}) does not have storage encryption enabled. "
110
+ f"Status: {db_status}"
111
+ )
112
+ compliance_status = ComplianceStatus.NON_COMPLIANT
113
+
114
+ return ComplianceResult(
115
+ resource_id=db_instance_id,
116
+ resource_type="AWS::RDS::DBInstance",
117
+ compliance_status=compliance_status,
118
+ evaluation_reason=evaluation_reason,
119
+ config_rule_name=self.rule_name,
120
+ region=region
121
+ )
122
+
123
+ def _get_rule_remediation_steps(self) -> List[str]:
124
+ """Get remediation steps for enabling RDS storage encryption."""
125
+ return [
126
+ "1. IMPORTANT: You CANNOT enable encryption on an existing unencrypted RDS instance.",
127
+ " You must create a new encrypted instance from a snapshot.",
128
+ "",
129
+ "2. Create an encrypted snapshot from the unencrypted instance:",
130
+ " # Create a snapshot of the unencrypted instance",
131
+ " aws rds create-db-snapshot \\",
132
+ " --db-instance-identifier <unencrypted-instance-id> \\",
133
+ " --db-snapshot-identifier <snapshot-name> \\",
134
+ " --region <region>",
135
+ "",
136
+ " # Wait for snapshot to complete",
137
+ " aws rds wait db-snapshot-completed \\",
138
+ " --db-snapshot-identifier <snapshot-name> \\",
139
+ " --region <region>",
140
+ "",
141
+ " # Copy snapshot with encryption enabled",
142
+ " aws rds copy-db-snapshot \\",
143
+ " --source-db-snapshot-identifier <snapshot-name> \\",
144
+ " --target-db-snapshot-identifier <encrypted-snapshot-name> \\",
145
+ " --kms-key-id <kms-key-id> \\",
146
+ " --region <region>",
147
+ "",
148
+ "3. Restore a new encrypted instance from the encrypted snapshot:",
149
+ " aws rds restore-db-instance-from-db-snapshot \\",
150
+ " --db-instance-identifier <new-encrypted-instance-id> \\",
151
+ " --db-snapshot-identifier <encrypted-snapshot-name> \\",
152
+ " --db-instance-class <instance-class> \\",
153
+ " --region <region>",
154
+ "",
155
+ "4. Update application connection strings to point to the new instance:",
156
+ " # Get the new endpoint",
157
+ " aws rds describe-db-instances \\",
158
+ " --db-instance-identifier <new-encrypted-instance-id> \\",
159
+ " --query 'DBInstances[0].Endpoint.Address' \\",
160
+ " --output text \\",
161
+ " --region <region>",
162
+ "",
163
+ "5. Test the new encrypted instance thoroughly:",
164
+ " - Verify application connectivity",
165
+ " - Verify data integrity",
166
+ " - Verify performance is acceptable",
167
+ " - Test backup and restore procedures",
168
+ "",
169
+ "6. After successful testing, delete the old unencrypted instance:",
170
+ " # Create a final backup before deletion (optional)",
171
+ " aws rds create-db-snapshot \\",
172
+ " --db-instance-identifier <unencrypted-instance-id> \\",
173
+ " --db-snapshot-identifier <final-backup-snapshot> \\",
174
+ " --region <region>",
175
+ "",
176
+ " # Delete the unencrypted instance",
177
+ " aws rds delete-db-instance \\",
178
+ " --db-instance-identifier <unencrypted-instance-id> \\",
179
+ " --skip-final-snapshot \\", # or --final-db-snapshot-identifier <name>",
180
+ " --region <region>",
181
+ "",
182
+ "7. For NEW instances, always enable encryption at creation:",
183
+ " aws rds create-db-instance \\",
184
+ " --db-instance-identifier <instance-id> \\",
185
+ " --db-instance-class <instance-class> \\",
186
+ " --engine <engine> \\",
187
+ " --master-username <username> \\",
188
+ " --master-user-password <password> \\",
189
+ " --allocated-storage <size-in-gb> \\",
190
+ " --storage-encrypted \\",
191
+ " --kms-key-id <kms-key-id> \\", # Optional, uses default if omitted",
192
+ " --region <region>",
193
+ "",
194
+ "8. Best practices:",
195
+ " - Use customer-managed KMS keys for better control",
196
+ " - Enable automatic key rotation for KMS keys",
197
+ " - Grant appropriate IAM permissions for KMS key usage",
198
+ " - Ensure read replicas are also encrypted (automatic if source is encrypted)",
199
+ " - Document the KMS key used for each database",
200
+ " - Test disaster recovery procedures with encrypted instances",
201
+ "",
202
+ "9. Console method:",
203
+ " - Navigate to RDS service",
204
+ " - Select the unencrypted instance",
205
+ " - Actions > Take snapshot",
206
+ " - After snapshot completes, select it",
207
+ " - Actions > Copy snapshot",
208
+ " - Check 'Enable encryption' and select KMS key",
209
+ " - After copy completes, select encrypted snapshot",
210
+ " - Actions > Restore snapshot",
211
+ " - Configure new instance settings",
212
+ " - Launch the encrypted instance",
213
+ "",
214
+ "10. Important notes:",
215
+ " - Encryption cannot be removed once enabled",
216
+ " - Encrypted instances can only be restored to encrypted instances",
217
+ " - Read replicas inherit encryption from source instance",
218
+ " - Snapshots inherit encryption from source instance",
219
+ " - Cross-region snapshot copies can change encryption settings",
220
+ " - Performance impact is negligible",
221
+ " - No additional cost for encryption (KMS key costs apply)",
222
+ "",
223
+ "Priority: HIGH - Database encryption is critical for compliance and data protection",
224
+ "Effort: High - Requires creating new instance and migrating data",
225
+ "",
226
+ "AWS Documentation:",
227
+ "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.html"
228
+ ]
@@ -0,0 +1,383 @@
1
+ """
2
+ CIS Control 3.11 - S3 Default Encryption with KMS
3
+ Ensures S3 buckets have default encryption enabled with customer-managed KMS keys for data at rest protection.
4
+ """
5
+
6
+ import logging
7
+ from typing import List, Dict, Any
8
+ from botocore.exceptions import ClientError
9
+
10
+ from aws_cis_assessment.controls.base_control import BaseConfigRuleAssessment
11
+ from aws_cis_assessment.core.models import ComplianceResult, ComplianceStatus
12
+ from aws_cis_assessment.core.aws_client_factory import AWSClientFactory
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class S3DefaultEncryptionKMSAssessment(BaseConfigRuleAssessment):
18
+ """
19
+ CIS Control 3.11 - Encrypt Sensitive Data at Rest
20
+ AWS Config Rule: s3-default-encryption-kms
21
+
22
+ Ensures S3 buckets have default encryption enabled with customer-managed KMS keys.
23
+ While SSE-S3 provides encryption, using customer-managed KMS keys provides
24
+ better control, auditability, and compliance capabilities.
25
+ """
26
+
27
+ def __init__(self):
28
+ super().__init__(
29
+ rule_name="s3-default-encryption-kms",
30
+ control_id="3.11",
31
+ resource_types=["AWS::S3::Bucket"]
32
+ )
33
+
34
+ def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
35
+ """Get all S3 buckets and their encryption configuration."""
36
+ if resource_type != "AWS::S3::Bucket":
37
+ return []
38
+
39
+ try:
40
+ # S3 is a global service, but we only want to list buckets once
41
+ # Only process in us-east-1 to avoid duplicate evaluations
42
+ if region != 'us-east-1':
43
+ return []
44
+
45
+ s3_client = aws_factory.get_client('s3', region)
46
+
47
+ buckets = []
48
+
49
+ # List all buckets
50
+ response = s3_client.list_buckets()
51
+ bucket_list = response.get('Buckets', [])
52
+
53
+ # Get encryption configuration for each bucket
54
+ for bucket in bucket_list:
55
+ bucket_name = bucket.get('Name', '')
56
+
57
+ try:
58
+ # Get bucket encryption configuration
59
+ try:
60
+ encryption_response = s3_client.get_bucket_encryption(Bucket=bucket_name)
61
+ rules = encryption_response.get('ServerSideEncryptionConfiguration', {}).get('Rules', [])
62
+
63
+ if rules:
64
+ # Get the first rule (typically only one)
65
+ rule = rules[0]
66
+ sse_algorithm = rule.get('ApplyServerSideEncryptionByDefault', {}).get('SSEAlgorithm', '')
67
+ kms_key_id = rule.get('ApplyServerSideEncryptionByDefault', {}).get('KMSMasterKeyID', '')
68
+ bucket_key_enabled = rule.get('BucketKeyEnabled', False)
69
+
70
+ # Determine encryption type
71
+ if sse_algorithm == 'aws:kms':
72
+ encryption_type = 'KMS'
73
+ encrypted_with_kms = True
74
+ elif sse_algorithm == 'AES256':
75
+ encryption_type = 'SSE-S3'
76
+ encrypted_with_kms = False
77
+ else:
78
+ encryption_type = sse_algorithm
79
+ encrypted_with_kms = False
80
+ else:
81
+ encryption_type = 'NONE'
82
+ encrypted_with_kms = False
83
+ kms_key_id = ''
84
+ bucket_key_enabled = False
85
+
86
+ except ClientError as e:
87
+ error_code = e.response.get('Error', {}).get('Code', '')
88
+ if error_code == 'ServerSideEncryptionConfigurationNotFoundError':
89
+ # No encryption configured
90
+ encryption_type = 'NONE'
91
+ encrypted_with_kms = False
92
+ kms_key_id = ''
93
+ bucket_key_enabled = False
94
+ else:
95
+ # Access denied or other error
96
+ logger.warning(f"Error getting encryption for bucket {bucket_name}: {e}")
97
+ continue
98
+
99
+ # Get bucket location
100
+ try:
101
+ location_response = s3_client.get_bucket_location(Bucket=bucket_name)
102
+ bucket_region = location_response.get('LocationConstraint') or 'us-east-1'
103
+ except ClientError:
104
+ bucket_region = 'unknown'
105
+
106
+ buckets.append({
107
+ 'BucketName': bucket_name,
108
+ 'EncryptionType': encryption_type,
109
+ 'EncryptedWithKMS': encrypted_with_kms,
110
+ 'KMSMasterKeyID': kms_key_id,
111
+ 'BucketKeyEnabled': bucket_key_enabled,
112
+ 'BucketRegion': bucket_region,
113
+ 'CreationDate': bucket.get('CreationDate', '')
114
+ })
115
+
116
+ except ClientError as e:
117
+ error_code = e.response.get('Error', {}).get('Code', '')
118
+ if error_code in ['NoSuchBucket', 'AccessDenied']:
119
+ logger.debug(f"Cannot access bucket {bucket_name}: {error_code}")
120
+ else:
121
+ logger.warning(f"Error processing bucket {bucket_name}: {e}")
122
+ continue
123
+
124
+ logger.debug(f"Found {len(buckets)} S3 buckets")
125
+ return buckets
126
+
127
+ except ClientError as e:
128
+ error_code = e.response.get('Error', {}).get('Code', '')
129
+ if error_code == 'AccessDenied':
130
+ logger.warning(f"Access denied to list S3 buckets")
131
+ else:
132
+ logger.error(f"Error retrieving S3 buckets: {e}")
133
+ return []
134
+ except Exception as e:
135
+ logger.error(f"Unexpected error retrieving S3 buckets: {e}")
136
+ return []
137
+
138
+ def _evaluate_resource_compliance(
139
+ self,
140
+ resource: Dict[str, Any],
141
+ aws_factory: AWSClientFactory,
142
+ region: str
143
+ ) -> ComplianceResult:
144
+ """Evaluate if S3 bucket has default encryption enabled with KMS."""
145
+ bucket_name = resource.get('BucketName', 'unknown')
146
+ encrypted_with_kms = resource.get('EncryptedWithKMS', False)
147
+ encryption_type = resource.get('EncryptionType', 'NONE')
148
+ kms_key_id = resource.get('KMSMasterKeyID', '')
149
+ bucket_key_enabled = resource.get('BucketKeyEnabled', False)
150
+ bucket_region = resource.get('BucketRegion', 'unknown')
151
+
152
+ # Check if bucket is encrypted with customer-managed KMS key
153
+ is_compliant = encrypted_with_kms
154
+
155
+ if is_compliant:
156
+ bucket_key_info = " (S3 Bucket Key enabled)" if bucket_key_enabled else ""
157
+ if kms_key_id:
158
+ evaluation_reason = (
159
+ f"S3 bucket '{bucket_name}' (region: {bucket_region}) has default encryption "
160
+ f"enabled with customer-managed KMS key: {kms_key_id}{bucket_key_info}"
161
+ )
162
+ else:
163
+ evaluation_reason = (
164
+ f"S3 bucket '{bucket_name}' (region: {bucket_region}) has default encryption "
165
+ f"enabled with KMS{bucket_key_info}"
166
+ )
167
+ compliance_status = ComplianceStatus.COMPLIANT
168
+ else:
169
+ if encryption_type == 'SSE-S3':
170
+ evaluation_reason = (
171
+ f"S3 bucket '{bucket_name}' (region: {bucket_region}) is encrypted with SSE-S3 (AES256). "
172
+ f"Consider using customer-managed KMS keys for better control and auditability."
173
+ )
174
+ elif encryption_type == 'NONE':
175
+ evaluation_reason = (
176
+ f"S3 bucket '{bucket_name}' (region: {bucket_region}) does not have default encryption enabled."
177
+ )
178
+ else:
179
+ evaluation_reason = (
180
+ f"S3 bucket '{bucket_name}' (region: {bucket_region}) is not encrypted with "
181
+ f"customer-managed KMS key. Current encryption: {encryption_type}"
182
+ )
183
+ compliance_status = ComplianceStatus.NON_COMPLIANT
184
+
185
+ return ComplianceResult(
186
+ resource_id=bucket_name,
187
+ resource_type="AWS::S3::Bucket",
188
+ compliance_status=compliance_status,
189
+ evaluation_reason=evaluation_reason,
190
+ config_rule_name=self.rule_name,
191
+ region=region
192
+ )
193
+
194
+ def _get_rule_remediation_steps(self) -> List[str]:
195
+ """Get remediation steps for enabling S3 KMS encryption."""
196
+ return [
197
+ "1. Enable KMS encryption on an existing S3 bucket using AWS CLI:",
198
+ " aws s3api put-bucket-encryption \\",
199
+ " --bucket <bucket-name> \\",
200
+ " --server-side-encryption-configuration '{",
201
+ ' "Rules": [{',
202
+ ' "ApplyServerSideEncryptionByDefault": {',
203
+ ' "SSEAlgorithm": "aws:kms",',
204
+ ' "KMSMasterKeyID": "<kms-key-id>"',
205
+ " },",
206
+ ' "BucketKeyEnabled": true',
207
+ " }]",
208
+ " }'",
209
+ "",
210
+ "2. Enable KMS encryption with S3 Bucket Key (recommended for cost savings):",
211
+ " # S3 Bucket Key reduces KMS API calls by up to 99%",
212
+ " aws s3api put-bucket-encryption \\",
213
+ " --bucket <bucket-name> \\",
214
+ " --server-side-encryption-configuration '{",
215
+ ' "Rules": [{',
216
+ ' "ApplyServerSideEncryptionByDefault": {',
217
+ ' "SSEAlgorithm": "aws:kms",',
218
+ ' "KMSMasterKeyID": "arn:aws:kms:<region>:<account-id>:key/<key-id>"',
219
+ " },",
220
+ ' "BucketKeyEnabled": true',
221
+ " }]",
222
+ " }'",
223
+ "",
224
+ "3. Verify encryption configuration:",
225
+ " aws s3api get-bucket-encryption \\",
226
+ " --bucket <bucket-name>",
227
+ "",
228
+ "4. Console method:",
229
+ " - Navigate to S3 service",
230
+ " - Select the bucket",
231
+ " - Click 'Properties' tab",
232
+ " - Scroll to 'Default encryption'",
233
+ " - Click 'Edit'",
234
+ " - Select 'Server-side encryption with AWS Key Management Service keys (SSE-KMS)'",
235
+ " - Choose 'AWS KMS key'",
236
+ " - Select 'Choose from your AWS KMS keys' or 'Enter AWS KMS key ARN'",
237
+ " - Select or enter your KMS key",
238
+ " - Check 'Bucket Key' (recommended)",
239
+ " - Click 'Save changes'",
240
+ "",
241
+ "5. Create a customer-managed KMS key for S3 if you don't have one:",
242
+ " aws kms create-key \\",
243
+ " --description 'S3 bucket encryption key' \\",
244
+ " --key-policy file://s3-key-policy.json \\",
245
+ " --region <region>",
246
+ "",
247
+ " # Create an alias for easier reference",
248
+ " aws kms create-alias \\",
249
+ " --alias-name alias/s3-encryption \\",
250
+ " --target-key-id <key-id> \\",
251
+ " --region <region>",
252
+ "",
253
+ "6. Example KMS key policy for S3 (s3-key-policy.json):",
254
+ " {",
255
+ ' "Version": "2012-10-17",',
256
+ ' "Statement": [',
257
+ " {",
258
+ ' "Sid": "Enable IAM User Permissions",',
259
+ ' "Effect": "Allow",',
260
+ ' "Principal": {"AWS": "arn:aws:iam::<account-id>:root"},',
261
+ ' "Action": "kms:*",',
262
+ ' "Resource": "*"',
263
+ " },",
264
+ " {",
265
+ ' "Sid": "Allow S3 to use the key",',
266
+ ' "Effect": "Allow",',
267
+ ' "Principal": {"Service": "s3.amazonaws.com"},',
268
+ ' "Action": [',
269
+ ' "kms:Decrypt",',
270
+ ' "kms:GenerateDataKey"',
271
+ " ],",
272
+ ' "Resource": "*"',
273
+ " },",
274
+ " {",
275
+ ' "Sid": "Allow users to encrypt/decrypt objects",',
276
+ ' "Effect": "Allow",',
277
+ ' "Principal": {"AWS": "arn:aws:iam::<account-id>:root"},',
278
+ ' "Action": [',
279
+ ' "kms:Decrypt",',
280
+ ' "kms:GenerateDataKey",',
281
+ ' "kms:DescribeKey"',
282
+ " ],",
283
+ ' "Resource": "*",',
284
+ ' "Condition": {',
285
+ ' "StringEquals": {',
286
+ ' "kms:ViaService": "s3.<region>.amazonaws.com"',
287
+ " }",
288
+ " }",
289
+ " }",
290
+ " ]",
291
+ " }",
292
+ "",
293
+ "7. Enable encryption on multiple buckets (script):",
294
+ " #!/bin/bash",
295
+ " KMS_KEY_ID='<kms-key-id>'",
296
+ " ",
297
+ " # Get all bucket names",
298
+ " aws s3api list-buckets --query 'Buckets[].Name' --output text | \\",
299
+ " while read bucket; do",
300
+ " echo \"Enabling encryption on $bucket\"",
301
+ " aws s3api put-bucket-encryption \\",
302
+ " --bucket \"$bucket\" \\",
303
+ " --server-side-encryption-configuration '{",
304
+ ' "Rules": [{',
305
+ ' "ApplyServerSideEncryptionByDefault": {',
306
+ ' "SSEAlgorithm": "aws:kms",',
307
+ ' "KMSMasterKeyID": "'\"$KMS_KEY_ID\"'"',
308
+ " },",
309
+ ' "BucketKeyEnabled": true',
310
+ " }]",
311
+ " }'",
312
+ " done",
313
+ "",
314
+ "8. Best practices:",
315
+ " - Use customer-managed KMS keys for all production buckets",
316
+ " - Enable S3 Bucket Key to reduce KMS costs (up to 99% reduction)",
317
+ " - Enable automatic key rotation for KMS keys",
318
+ " - Use separate KMS keys for different data classifications",
319
+ " - Grant appropriate IAM permissions for KMS key usage",
320
+ " - Document the KMS key used for each bucket",
321
+ " - Monitor KMS key usage with CloudWatch",
322
+ " - Set up CloudTrail logging for KMS key operations",
323
+ " - Consider using bucket policies to enforce encryption",
324
+ "",
325
+ "9. Enforce encryption with bucket policy (deny unencrypted uploads):",
326
+ " {",
327
+ ' "Version": "2012-10-17",',
328
+ ' "Statement": [{',
329
+ ' "Sid": "DenyUnencryptedObjectUploads",',
330
+ ' "Effect": "Deny",',
331
+ ' "Principal": "*",',
332
+ ' "Action": "s3:PutObject",',
333
+ ' "Resource": "arn:aws:s3:::<bucket-name>/*",',
334
+ ' "Condition": {',
335
+ ' "StringNotEquals": {',
336
+ ' "s3:x-amz-server-side-encryption": "aws:kms"',
337
+ " }",
338
+ " }",
339
+ " }]",
340
+ " }",
341
+ "",
342
+ "10. Important notes:",
343
+ " - Default encryption applies to NEW objects only",
344
+ " - Existing objects remain with their current encryption",
345
+ " - To encrypt existing objects, copy them in place:",
346
+ " aws s3 cp s3://<bucket>/ s3://<bucket>/ \\",
347
+ " --recursive \\",
348
+ " --sse aws:kms \\",
349
+ " --sse-kms-key-id <kms-key-id> \\",
350
+ " --metadata-directive REPLACE",
351
+ " - S3 Bucket Key significantly reduces KMS costs",
352
+ " - Performance impact is negligible",
353
+ " - KMS key must be in the same region as the bucket",
354
+ " - Cross-region replication can use different KMS keys",
355
+ "",
356
+ "11. Cost considerations:",
357
+ " - SSE-S3: No additional cost",
358
+ " - SSE-KMS without Bucket Key: $0.03 per 10,000 requests",
359
+ " - SSE-KMS with Bucket Key: Up to 99% cost reduction",
360
+ " - Customer-managed KMS keys: $1/month per key",
361
+ " - Always enable S3 Bucket Key to minimize costs",
362
+ "",
363
+ "12. Verify encryption after configuration:",
364
+ " # Check bucket encryption",
365
+ " aws s3api get-bucket-encryption \\",
366
+ " --bucket <bucket-name> \\",
367
+ " --query 'ServerSideEncryptionConfiguration.Rules[0]' \\",
368
+ " --output table",
369
+ "",
370
+ " # Upload a test object and verify it's encrypted",
371
+ " echo 'test' > test.txt",
372
+ " aws s3 cp test.txt s3://<bucket-name>/",
373
+ " aws s3api head-object \\",
374
+ " --bucket <bucket-name> \\",
375
+ " --key test.txt \\",
376
+ " --query '{Encryption:ServerSideEncryption,KMSKeyId:SSEKMSKeyId}'",
377
+ "",
378
+ "Priority: HIGH - S3 encryption is critical for data protection and compliance",
379
+ "Effort: Low - Can be enabled with a single API call, no downtime",
380
+ "",
381
+ "AWS Documentation:",
382
+ "https://docs.aws.amazon.com/AmazonS3/latest/userguide/default-bucket-encryption.html"
383
+ ]