Skip to content

Commit 742f13a

Browse files
committed
added kb s3 bucket check rule
1 parent de6e5de commit 742f13a

File tree

4 files changed

+189
-0
lines changed

4 files changed

+189
-0
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
"""Config rule to check knowledge base S3 bucket configuration for Bedrock environments.
2+
3+
Version: 1.0
4+
5+
Config rule for SRA in the repo, https://github.com/aws-samples/aws-security-reference-architecture-examples
6+
7+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
8+
SPDX-License-Identifier: MIT-0
9+
"""
10+
import json
11+
import logging
12+
import os
13+
from typing import Any
14+
15+
import boto3
16+
from botocore.exceptions import ClientError
17+
18+
# Setup Default Logger
19+
LOGGER = logging.getLogger(__name__)
20+
log_level = os.environ.get("LOG_LEVEL", logging.INFO)
21+
LOGGER.setLevel(log_level)
22+
LOGGER.info(f"boto3 version: {boto3.__version__}")
23+
24+
# Get AWS region from environment variable
25+
AWS_REGION = os.environ.get("AWS_REGION")
26+
27+
# Initialize AWS clients
28+
bedrock_agent_client = boto3.client("bedrock-agent", region_name=AWS_REGION)
29+
s3_client = boto3.client("s3", region_name=AWS_REGION)
30+
config_client = boto3.client("config", region_name=AWS_REGION)
31+
32+
def evaluate_compliance(rule_parameters: dict) -> tuple[str, str]:
33+
"""Evaluate if Bedrock Knowledge Base S3 bucket has required configurations.
34+
35+
Args:
36+
rule_parameters (dict): Rule parameters from AWS Config rule.
37+
38+
Returns:
39+
tuple[str, str]: Compliance status and annotation
40+
"""
41+
try:
42+
# Get all knowledge bases
43+
non_compliant_buckets = []
44+
paginator = bedrock_agent_client.get_paginator("list_knowledge_bases")
45+
46+
for page in paginator.paginate():
47+
for kb in page["knowledgeBaseSummaries"]:
48+
kb_details = bedrock_agent_client.get_knowledge_base(knowledgeBaseId=kb["knowledgeBaseId"])
49+
data_source = bedrock_agent_client.get_data_source(
50+
knowledgeBaseId=kb["knowledgeBaseId"],
51+
dataSourceId=kb_details["dataSource"]["dataSourceId"]
52+
)
53+
54+
# Extract bucket name from S3 path
55+
s3_path = data_source["configuration"]["s3Configuration"]["bucketName"]
56+
bucket_name = s3_path.split("/")[0]
57+
58+
issues = []
59+
60+
# Check retention
61+
if rule_parameters.get("check_retention", "true").lower() == "true":
62+
try:
63+
lifecycle = s3_client.get_bucket_lifecycle_configuration(Bucket=bucket_name)
64+
if not any(rule.get("Expiration") for rule in lifecycle.get("Rules", [])):
65+
issues.append("retention")
66+
except ClientError as e:
67+
if e.response["Error"]["Code"] == "NoSuchLifecycleConfiguration":
68+
issues.append("retention")
69+
70+
# Check encryption
71+
if rule_parameters.get("check_encryption", "true").lower() == "true":
72+
try:
73+
encryption = s3_client.get_bucket_encryption(Bucket=bucket_name)
74+
if not encryption.get("ServerSideEncryptionConfiguration"):
75+
issues.append("encryption")
76+
except ClientError:
77+
issues.append("encryption")
78+
79+
# Check server access logging
80+
if rule_parameters.get("check_access_logging", "true").lower() == "true":
81+
logging_config = s3_client.get_bucket_logging(Bucket=bucket_name)
82+
if not logging_config.get("LoggingEnabled"):
83+
issues.append("access logging")
84+
85+
# Check object lock
86+
if rule_parameters.get("check_object_locking", "true").lower() == "true":
87+
try:
88+
lock_config = s3_client.get_bucket_object_lock_configuration(Bucket=bucket_name)
89+
if not lock_config.get("ObjectLockConfiguration"):
90+
issues.append("object locking")
91+
except ClientError:
92+
issues.append("object locking")
93+
94+
# Check versioning
95+
if rule_parameters.get("check_versioning", "true").lower() == "true":
96+
versioning = s3_client.get_bucket_versioning(Bucket=bucket_name)
97+
if versioning.get("Status") != "Enabled":
98+
issues.append("versioning")
99+
100+
if issues:
101+
non_compliant_buckets.append(f"{bucket_name} (missing: {', '.join(issues)})")
102+
103+
if non_compliant_buckets:
104+
return "NON_COMPLIANT", f"The following KB S3 buckets are non-compliant: {'; '.join(non_compliant_buckets)}"
105+
return "COMPLIANT", "All Knowledge Base S3 buckets meet the required configurations"
106+
107+
except Exception as e:
108+
LOGGER.error(f"Error evaluating Knowledge Base S3 bucket configurations: {str(e)}")
109+
return "ERROR", f"Error evaluating compliance: {str(e)}"
110+
111+
def lambda_handler(event: dict, context: Any) -> None:
112+
"""Lambda handler.
113+
114+
Args:
115+
event (dict): Lambda event object
116+
context (Any): Lambda context object
117+
"""
118+
LOGGER.info("Evaluating compliance for AWS Config rule")
119+
LOGGER.info(f"Event: {json.dumps(event)}")
120+
121+
invoking_event = json.loads(event["invokingEvent"])
122+
rule_parameters = json.loads(event["ruleParameters"]) if "ruleParameters" in event else {}
123+
124+
compliance_type, annotation = evaluate_compliance(rule_parameters)
125+
126+
evaluation = {
127+
"ComplianceResourceType": "AWS::::Account",
128+
"ComplianceResourceId": event["accountId"],
129+
"ComplianceType": compliance_type,
130+
"Annotation": annotation,
131+
"OrderingTimestamp": invoking_event["notificationCreationTime"],
132+
}
133+
134+
LOGGER.info(f"Compliance evaluation result: {compliance_type}")
135+
LOGGER.info(f"Annotation: {annotation}")
136+
137+
config_client.put_evaluations(Evaluations=[evaluation], ResultToken=event["resultToken"])
138+
139+
LOGGER.info("Compliance evaluation complete.")

aws_sra_examples/solutions/genai/bedrock_org/lambda/src/app.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ def load_sra_cloudwatch_dashboard() -> dict:
204204
+ r'\[((?:"[a-z0-9-]+"(?:\s*,\s*)?)*)\],\s*"input_params"\s*:\s*(\{\})\}$',
205205
"SRA-BEDROCK-CHECK-KB-INGESTION-ENCRYPTION": r'^\{"deploy"\s*:\s*"(true|false)",\s*"accounts"\s*:\s*\[((?:"[0-9]+"(?:\s*,\s*)?)*)\],\s*"regions"\s*:\s*'
206206
+ r'\[((?:"[a-z0-9-]+"(?:\s*,\s*)?)*)\],\s*"input_params"\s*:\s*(\{\})\}$',
207+
"SRA-BEDROCK-CHECK-KB-S3-BUCKET": r'^\{"deploy"\s*:\s*"(true|false)",\s*"accounts"\s*:\s*\[((?:"[0-9]+"(?:\s*,\s*)?)*)\],\s*"regions"\s*:\s*'
208+
+ r'\[((?:"[a-z0-9-]+"(?:\s*,\s*)?)*)\],\s*"input_params"\s*:\s*\{(\s*"check_retention"\s*:\s*"(true|false)")?(\s*,\s*"check_encryption"\s*:\s*'
209+
+ r'"(true|false)")?(\s*,\s*"check_access_logging"\s*:\s*"(true|false)")?(\s*,\s*"check_object_locking"\s*:\s*"(true|false)")?(\s*,\s*'
210+
+ r'"check_versioning"\s*:\s*"(true|false)")?\s*\}\}$',
207211
}
208212

209213
# Instantiate sra class objects

aws_sra_examples/solutions/genai/bedrock_org/lambda/src/sra_config_lambda_iam_permissions.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,5 +153,33 @@
153153
"Resource": "*"
154154
}
155155
]
156+
},
157+
"sra-bedrock-check-kb-s3-bucket": {
158+
"Version": "2012-10-17",
159+
"Statement": [
160+
{
161+
"Sid": "AllowKnowledgeBaseAccess",
162+
"Effect": "Allow",
163+
"Action": [
164+
"bedrock:ListKnowledgeBases",
165+
"bedrock:GetKnowledgeBase",
166+
"bedrock:ListDataSources",
167+
"bedrock:GetDataSource"
168+
],
169+
"Resource": "*"
170+
},
171+
{
172+
"Sid": "AllowS3BucketAccess",
173+
"Effect": "Allow",
174+
"Action": [
175+
"s3:GetBucketLifecycleConfiguration",
176+
"s3:GetBucketEncryption",
177+
"s3:GetBucketLogging",
178+
"s3:GetBucketObjectLockConfiguration",
179+
"s3:GetBucketVersioning"
180+
],
181+
"Resource": "arn:aws:s3:::*"
182+
}
183+
]
156184
}
157185
}

aws_sra_examples/solutions/genai/bedrock_org/templates/sra-bedrock-org-main.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,20 @@ Parameters:
303303
Example: {\"deploy\": \"true\", \"accounts\": [\"123456789012\"], \"regions\": [\"us-east-1\"], \"input_params\": {}} or
304304
{\"deploy\": \"false\", \"accounts\": [], \"regions\": [], \"input_params\": {}}"
305305

306+
pBedrockKBS3BucketRuleParams:
307+
Type: String
308+
Default: '{"deploy": "true", "accounts": ["444455556666"], "regions": ["us-west-2"], "input_params": {"check_retention": "true", "check_encryption": "true", "check_access_logging": "true", "check_object_locking": "true", "check_versioning": "true"}}'
309+
Description: Bedrock Knowledge Base S3 Bucket Config Rule Parameters
310+
AllowedPattern: ^\{"deploy"\s*:\s*"(true|false)",\s*"accounts"\s*:\s*\[((?:"[0-9]+"(?:\s*,\s*)?)*)\],\s*"regions"\s*:\s*\[((?:"[a-z0-9-]+"(?:\s*,\s*)?)*)\],\s*"input_params"\s*:\s*\{(\s*"check_retention"\s*:\s*"(true|false)")?(\s*,\s*"check_encryption"\s*:\s*"(true|false)")?(\s*,\s*"check_access_logging"\s*:\s*"(true|false)")?(\s*,\s*"check_object_locking"\s*:\s*"(true|false)")?(\s*,\s*"check_versioning"\s*:\s*"(true|false)")?\s*\}\}$
311+
ConstraintDescription: >
312+
Must be a valid JSON string containing: 'deploy' (true/false), 'accounts' (array of account numbers),
313+
'regions' (array of region names), and 'input_params' object with optional parameters:
314+
'check_retention', 'check_encryption', 'check_access_logging', 'check_object_locking', 'check_versioning'.
315+
Each parameter in 'input_params' should be either "true" or "false".
316+
Arrays can be empty.
317+
Example: {"deploy": "true", "accounts": ["123456789012"], "regions": ["us-east-1"], "input_params": {"check_retention": "true", "check_encryption": "true", "check_access_logging": "true", "check_object_locking": "true", "check_versioning": "true"}} or
318+
{"deploy": "false", "accounts": [], "regions": [], "input_params": {}}
319+
306320
Metadata:
307321
AWS::CloudFormation::Interface:
308322
ParameterGroups:
@@ -345,6 +359,7 @@ Metadata:
345359
- pBedrockGuardrailEncryptionRuleParams
346360
- pBedrockKBLoggingRuleParams
347361
- pBedrockKBIngestionEncryptionRuleParams
362+
- pBedrockKBS3BucketRuleParams
348363
- Label:
349364
default: Bedrock CloudWatch Metric Filters
350365
Parameters:
@@ -416,6 +431,8 @@ Metadata:
416431
default: Bedrock Knowledge Base Logging Config Rule Parameters
417432
pBedrockKBIngestionEncryptionRuleParams:
418433
default: Bedrock Knowledge Base Data Ingestion Encryption Config Rule Parameters
434+
pBedrockKBS3BucketRuleParams:
435+
default: Bedrock Knowledge Base S3 Bucket Config Rule Parameters
419436

420437
Resources:
421438
rBedrockOrgLambdaRole:
@@ -698,6 +715,7 @@ Resources:
698715
SRA-BEDROCK-CENTRAL-OBSERVABILITY: !Ref pBedrockCentralObservabilityParams
699716
SRA-BEDROCK-CHECK-KB-LOGGING: !Ref pBedrockKBLoggingRuleParams
700717
SRA-BEDROCK-CHECK-KB-INGESTION-ENCRYPTION: !Ref pBedrockKBIngestionEncryptionRuleParams
718+
SRA-BEDROCK-CHECK-KB-S3-BUCKET: !Ref pBedrockKBS3BucketRuleParams
701719

702720
rBedrockOrgLambdaInvokePermission:
703721
Type: AWS::Lambda::Permission

0 commit comments

Comments
 (0)