Skip to content

Commit 9f9a32b

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents c501e9f + 97dbc64 commit 9f9a32b

File tree

28 files changed

+1524
-18
lines changed

28 files changed

+1524
-18
lines changed

aws_sra_examples/solutions/genai/bedrock_org/README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [Security Controls](#security-controls)
88
- [JSON Parameters](#json-parameters)
99
- [References](#references)
10+
- [Related Security Control Solutions](#related-security-control-solutions)
1011

1112
---
1213

@@ -102,6 +103,11 @@ aws cloudformation create-stack \
102103
ParameterKey=pBedrockPromptInjectionFilterParams,ParameterValue='"{\"deploy\": \"true\", \"accounts\": [\"222222222222\",\"333333333333\"], \"regions\": [\"us-east-1\"], \"filter_params\": {\"log_group_name\": \"model-invocation-log-group\", \"input_path\": \"input.inputBodyJson.messages[0].content\"}}"' \
103104
ParameterKey=pBedrockSensitiveInfoFilterParams,ParameterValue='"{\"deploy\": \"true\", \"accounts\": [\"222222222222\",\"333333333333\"], \"regions\": [\"us-east-1\"], \"filter_params\": {\"log_group_name\": \"model-invocation-log-group\", \"input_path\": \"input.inputBodyJson.messages[0].content\"}}"' \
104105
ParameterKey=pBedrockCentralObservabilityParams,ParameterValue='"{\"deploy\": \"true\", \"bedrock_accounts\": [\"222222222222\",\"333333333333\"], \"regions\": [\"us-east-1\"]}"' \
106+
ParameterKey=pBedrockKBLoggingRuleParams,ParameterValue='"{\"deploy\": \"true\", \"accounts\": [\"222222222222\",\"333333333333\"], \"regions\": [\"us-east-1\",\"us-west-2\"], \"input_params\": {}}"' \
107+
ParameterKey=pBedrockKBIngestionEncryptionRuleParams,ParameterValue='"{\"deploy\": \"true\", \"accounts\": [\"222222222222\",\"333333333333\"], \"regions\": [\"us-east-1\",\"us-west-2\"], \"input_params\": {}}"' \
108+
ParameterKey=pBedrockKBS3BucketRuleParams,ParameterValue='"{\"deploy\": \"true\", \"accounts\": [\"222222222222\",\"333333333333\"], \"regions\": [\"us-east-1\",\"us-west-2\"], \"input_params\": {\"check_retention\": \"true\", \"check_encryption\": \"true\", \"check_access_logging\": \"true\", \"check_object_locking\": \"true\", \"check_versioning\": \"true\"}}"' \
109+
ParameterKey=pBedrockKBVectorStoreSecretRuleParams,ParameterValue='"{\"deploy\": \"true\", \"accounts\": [\"222222222222\",\"333333333333\"], \"regions\": [\"us-east-1\",\"us-west-2\"], \"input_params\": {}}"' \
110+
ParameterKey=pBedrockKBOpenSearchEncryptionRuleParams,ParameterValue='"{\"deploy\": \"true\", \"accounts\": [\"222222222222\",\"333333333333\"], \"regions\": [\"us-east-1\",\"us-west-2\"], \"input_params\": {}}"' \
105111
--capabilities CAPABILITY_NAMED_IAM
106112
```
107113

@@ -139,6 +145,11 @@ Please read the following notes before deploying the stack to ensure successful
139145
| CloudWatch Endpoint Validation | Ensures proper CloudWatch VPC endpoint setup | [pBedrockCWEndpointsRuleParams](#pbedrockcwendpointsruleparams) |
140146
| S3 Endpoint Validation | Ensures proper S3 VPC endpoint setup | [pBedrockS3EndpointsRuleParams](#pbedrocks3endpointsruleparams) |
141147
| Guardrail Encryption | Validates KMS encryption for Bedrock guardrails | [pBedrockGuardrailEncryptionRuleParams](#pbedrockguardrailencryptionruleparams) |
148+
| Knowledge Base Logging | Validates logging configuration for Bedrock Knowledge Base | [pBedrockKBLoggingRuleParams](#pbedrockkbloggingruleparams) |
149+
| Knowledge Base Ingestion Encryption | Validates encryption for Knowledge Base data ingestion | [pBedrockKBIngestionEncryptionRuleParams](#pbedrockkbingestionencryptionruleparams) |
150+
| Knowledge Base S3 Bucket | Validates S3 bucket configurations for Knowledge Base | [pBedrockKBS3BucketRuleParams](#pbedrockkbs3bucketruleparams) |
151+
| Knowledge Base Vector Store Secret | Validates vector store secret configuration | [pBedrockKBVectorStoreSecretRuleParams](#pbedrockkbvectorstoresecretruleparams) |
152+
| Knowledge Base OpenSearch Encryption | Validates OpenSearch encryption configuration | [pBedrockKBOpenSearchEncryptionRuleParams](#pbedrockkbopensearchencryptionruleparams) |
142153

143154
> **Important Note**: The Config rule Lambda execution role needs to have access to any KMS keys used to encrypt Bedrock guardrails. Make sure to grant the appropriate KMS key permissions to the Lambda role to ensure proper evaluation of encrypted guardrail configurations.
144155
@@ -155,6 +166,15 @@ Please read the following notes before deploying the stack to ensure successful
155166
|-----------------|-------------|----------------|
156167
| Central Observability | Configures cross-account/region metric aggregation | [pBedrockCentralObservabilityParams](#pbedrockcentralobservabilityparams) |
157168

169+
### Bedrock Knowledge Base
170+
| Security Control | Description | JSON Parameter |
171+
|-----------------|-------------|----------------|
172+
| KB Logging | Validates logging configuration for Bedrock Knowledge Base | [pBedrockKBLoggingRuleParams](#pbedrockkbloggingruleparams) |
173+
| KB Ingestion Encryption | Validates encryption configuration for Bedrock Knowledge Base | [pBedrockKBIngestionEncryptionRuleParams](#pbedrockkbingestionencryptionruleparams) |
174+
| KB S3 Bucket | Validates S3 bucket configuration for Bedrock Knowledge Base | [pBedrockKBS3BucketRuleParams](#pbedrockkbs3bucketruleparams) |
175+
| KB Vector Store Secret | Validates secret configuration for Bedrock Knowledge Base | [pBedrockKBVectorStoreSecretRuleParams](#pbedrockkbvectorstoresecretruleparams) |
176+
| KB OpenSearch Encryption | Validates encryption configuration for Bedrock Knowledge Base | [pBedrockKBOpenSearchEncryptionRuleParams](#pbedrockkbopensearchencryptionruleparams) |
177+
158178
---
159179
## JSON Parameters
160180

@@ -367,6 +387,72 @@ This section explains the parameters in the CloudFormation template that require
367387
}
368388
```
369389

390+
### `pBedrockKBLoggingRuleParams`
391+
- **Purpose**: Validates logging configuration for Bedrock Knowledge Base.
392+
- **Structure**:
393+
```json
394+
{
395+
"deploy": "true|false",
396+
"accounts": ["account_id1", "account_id2"],
397+
"regions": ["region1", "region2"],
398+
"input_params": {}
399+
}
400+
```
401+
402+
### `pBedrockKBIngestionEncryptionRuleParams`
403+
- **Purpose**: Validates encryption configuration for Bedrock Knowledge Base.
404+
- **Structure**:
405+
```json
406+
{
407+
"deploy": "true|false",
408+
"accounts": ["account_id1", "account_id2"],
409+
"regions": ["region1", "region2"],
410+
"input_params": {}
411+
}
412+
```
413+
414+
### `pBedrockKBS3BucketRuleParams`
415+
- **Purpose**: Validates S3 bucket configuration for Bedrock Knowledge Base.
416+
- **Structure**:
417+
```json
418+
{
419+
"deploy": "true|false",
420+
"accounts": ["account_id1", "account_id2"],
421+
"regions": ["region1", "region2"],
422+
"input_params": {
423+
"check_retention": "true|false",
424+
"check_encryption": "true|false",
425+
"check_access_logging": "true|false",
426+
"check_object_locking": "true|false",
427+
"check_versioning": "true|false"
428+
}
429+
}
430+
```
431+
432+
### `pBedrockKBVectorStoreSecretRuleParams`
433+
- **Purpose**: Validates secret configuration for Bedrock Knowledge Base.
434+
- **Structure**:
435+
```json
436+
{
437+
"deploy": "true|false",
438+
"accounts": ["account_id1", "account_id2"],
439+
"regions": ["region1", "region2"],
440+
"input_params": {}
441+
}
442+
```
443+
444+
### `pBedrockKBOpenSearchEncryptionRuleParams`
445+
- **Purpose**: Validates encryption configuration for Bedrock Knowledge Base.
446+
- **Structure**:
447+
```json
448+
{
449+
"deploy": "true|false",
450+
"accounts": ["account_id1", "account_id2"],
451+
"regions": ["region1", "region2"],
452+
"input_params": {}
453+
}
454+
```
455+
370456
---
371457
## References
372458
- [AWS SRA Generative AI Deep-Dive](https://docs.aws.amazon.com/prescriptive-guidance/latest/security-reference-architecture/gen-ai-sra.html)
@@ -375,3 +461,32 @@ This section explains the parameters in the CloudFormation template that require
375461
- [CloudWatch Metrics and Alarms](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html)
376462
- [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html)
377463
- [AWS KMS](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html)
464+
465+
## Related Security Control Solutions
466+
467+
This solution works in conjunction with other AWS SRA solutions to provide comprehensive security controls for Bedrock GenAI environments:
468+
469+
### Amazon Bedrock Guardrails Solution
470+
The [SRA Bedrock Guardrails solution](../../genai/bedrock_guardrails/README.md) provides automated deployment of Amazon Bedrock Guardrails across your organization. It supports:
471+
472+
- **Content Filters**: Block harmful content in inputs/outputs based on predefined categories (Hate, Insults, Sexual, Violence, Misconduct, Prompt Attack)
473+
- **Denied Topics**: Define and block undesirable topics
474+
- **Word Filters**: Block specific words, phrases, and profanity
475+
- **Sensitive Information Filters**: Block or mask PII and sensitive data
476+
- **Contextual Grounding**: Detect and filter hallucinations based on source grounding
477+
478+
The solution uses KMS encryption for enhanced security and requires proper IAM role configurations for users who need to invoke or manage guardrails.
479+
480+
### GuardDuty Malware Protection for S3
481+
The [SRA GuardDuty Malware Protection solution](../../guardduty/guardduty_malware_protection_for_s3/README.md) helps protect S3 buckets used in your Bedrock environment from malware. This is particularly important for:
482+
483+
- Model evaluation job buckets
484+
- Knowledge base data ingestion buckets
485+
- Model invocation logging buckets
486+
487+
The solution enables GuardDuty's malware scanning capabilities to detect malicious files that could be used in prompt injection attacks or compromise your GenAI applications.
488+
489+
These complementary solutions work together to provide defense-in-depth for your Bedrock GenAI environment:
490+
- This solution (SRA Bedrock Org) provides organizational security controls and monitoring
491+
- Bedrock Guardrails solution provides content and data security controls
492+
- GuardDuty Malware Protection ensures S3 bucket security against malware threats

aws_sra_examples/solutions/genai/bedrock_org/lambda/rules/sra_bedrock_check_cloudwatch_endpoints/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
88
SPDX-License-Identifier: MIT-0
99
"""
10+
1011
import json
1112
import logging
1213
import os

aws_sra_examples/solutions/genai/bedrock_org/lambda/rules/sra_bedrock_check_eval_job_bucket/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
88
SPDX-License-Identifier: MIT-0
99
"""
10+
1011
import ast
1112
import logging
1213
import os

aws_sra_examples/solutions/genai/bedrock_org/lambda/rules/sra_bedrock_check_guardrail_encryption/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
88
SPDX-License-Identifier: MIT-0
99
"""
10+
1011
import json
1112
import logging
1213
import os

aws_sra_examples/solutions/genai/bedrock_org/lambda/rules/sra_bedrock_check_guardrails/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
88
SPDX-License-Identifier: MIT-0
99
"""
10+
1011
import ast
1112
import json
1213
import logging

aws_sra_examples/solutions/genai/bedrock_org/lambda/rules/sra_bedrock_check_iam_user_access/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
88
SPDX-License-Identifier: MIT-0
99
"""
10+
1011
import json
1112
import logging
1213
import os

aws_sra_examples/solutions/genai/bedrock_org/lambda/rules/sra_bedrock_check_invocation_log_cloudwatch/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
88
SPDX-License-Identifier: MIT-0
99
"""
10+
1011
import json
1112
import logging
1213
import os

aws_sra_examples/solutions/genai/bedrock_org/lambda/rules/sra_bedrock_check_invocation_log_s3/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
88
SPDX-License-Identifier: MIT-0
99
"""
10+
1011
import json
1112
import logging
1213
import os
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
"""Config rule to check knowledge base data ingestion encryption 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+
11+
import json
12+
import logging
13+
import os
14+
from typing import Any
15+
16+
import boto3
17+
from botocore.exceptions import ClientError
18+
19+
# Setup Default Logger
20+
LOGGER = logging.getLogger(__name__)
21+
log_level = os.environ.get("LOG_LEVEL", logging.INFO)
22+
LOGGER.setLevel(log_level)
23+
LOGGER.info(f"boto3 version: {boto3.__version__}")
24+
25+
# Get AWS region from environment variable
26+
AWS_REGION = os.environ.get("AWS_REGION")
27+
28+
# Initialize AWS clients
29+
bedrock_agent_client = boto3.client("bedrock-agent", region_name=AWS_REGION)
30+
config_client = boto3.client("config", region_name=AWS_REGION)
31+
32+
33+
def check_data_sources(kb_id: str, kb_name: str) -> str | None: # type: ignore # noqa: CFQ004, CCR001
34+
"""Check if a knowledge base's data sources are encrypted with KMS during ingestion.
35+
36+
Args:
37+
kb_id (str): Knowledge base ID
38+
kb_name (str): Knowledge base name
39+
40+
Raises:
41+
ClientError: If there is an error checking the knowledge base
42+
43+
Returns:
44+
str | None: Error message if non-compliant, None if compliant
45+
"""
46+
try:
47+
data_sources = bedrock_agent_client.list_data_sources(knowledgeBaseId=kb_id)
48+
LOGGER.info(f"Data sources: {data_sources}")
49+
if not isinstance(data_sources, dict):
50+
return f"{kb_name}: Invalid response"
51+
52+
unencrypted_sources = []
53+
for source in data_sources.get("dataSourceSummaries", []):
54+
LOGGER.info(f"Source: {source}")
55+
if not isinstance(source, dict):
56+
continue
57+
58+
# Get the detailed data source configuration
59+
try:
60+
source_details = bedrock_agent_client.get_data_source(knowledgeBaseId=kb_id, dataSourceId=source["dataSourceId"])
61+
LOGGER.info(f"Source details: {source_details}")
62+
63+
# Check for KMS encryption configuration
64+
data_source = source_details.get("dataSource", {})
65+
encryption_config = data_source.get("serverSideEncryptionConfiguration", {})
66+
LOGGER.info(f"Encryption config: {encryption_config}")
67+
68+
# Check if KMS key is configured for encryption
69+
if not encryption_config.get("kmsKeyArn"):
70+
unencrypted_sources.append(source.get("name", source["dataSourceId"]))
71+
72+
except ClientError as e:
73+
LOGGER.error(f"Error getting data source details for {source.get('name', source['dataSourceId'])}: {str(e)}")
74+
if e.response["Error"]["Code"] == "AccessDeniedException":
75+
unencrypted_sources.append(f"{source.get('name', source['dataSourceId'])}")
76+
continue
77+
78+
if unencrypted_sources:
79+
return f"{kb_name}: {len(unencrypted_sources)} sources need CMK"
80+
return None
81+
except ClientError as e:
82+
LOGGER.error(f"Error checking data sources for knowledge base {kb_name}: {str(e)}")
83+
if e.response["Error"]["Code"] == "AccessDeniedException":
84+
return f"{kb_name}: Access denied"
85+
raise
86+
87+
88+
def evaluate_compliance(rule_parameters: dict) -> tuple[str, str]: # noqa: U100
89+
"""Evaluate if Bedrock Knowledge Base data sources are encrypted with KMS.
90+
91+
Args:
92+
rule_parameters (dict): Rule parameters from AWS Config rule.
93+
94+
Returns:
95+
tuple[str, str]: Compliance type and annotation message.
96+
"""
97+
try:
98+
non_compliant_kbs = []
99+
paginator = bedrock_agent_client.get_paginator("list_knowledge_bases")
100+
101+
for page in paginator.paginate():
102+
for kb in page["knowledgeBaseSummaries"]:
103+
kb_id = kb["knowledgeBaseId"]
104+
kb_name = kb.get("name", kb_id)
105+
error = check_data_sources(kb_id, kb_name)
106+
if error:
107+
non_compliant_kbs.append(error)
108+
109+
if non_compliant_kbs:
110+
msg = f"KBs missing Customer Managed Keys: {'; '.join(non_compliant_kbs)}"
111+
# Ensure annotation doesn't exceed 256 characters
112+
if len(msg) > 256:
113+
LOGGER.info(f"Full message truncated: {msg}")
114+
msg = msg[:220] + " (see CloudWatch logs for details)"
115+
return "NON_COMPLIANT", msg
116+
return "COMPLIANT", "All KB data sources use Customer Managed Keys"
117+
118+
except Exception as e:
119+
LOGGER.error(f"Error evaluating Bedrock Knowledge Base encryption: {str(e)}")
120+
return "ERROR", f"Error: {str(e)[:240]}"
121+
122+
123+
def lambda_handler(event: dict, context: Any) -> None: # noqa: U100
124+
"""Lambda handler.
125+
126+
Args:
127+
event (dict): Lambda event object
128+
context (Any): Lambda context object
129+
"""
130+
LOGGER.info("Evaluating compliance for AWS Config rule")
131+
LOGGER.info(f"Event: {json.dumps(event)}")
132+
133+
invoking_event = json.loads(event["invokingEvent"])
134+
rule_parameters = json.loads(event["ruleParameters"]) if "ruleParameters" in event else {}
135+
136+
compliance_type, annotation = evaluate_compliance(rule_parameters)
137+
138+
evaluation = {
139+
"ComplianceResourceType": "AWS::::Account",
140+
"ComplianceResourceId": event["accountId"],
141+
"ComplianceType": compliance_type,
142+
"Annotation": annotation,
143+
"OrderingTimestamp": invoking_event["notificationCreationTime"],
144+
}
145+
146+
LOGGER.info(f"Compliance evaluation result: {compliance_type}")
147+
LOGGER.info(f"Annotation: {annotation}")
148+
149+
config_client.put_evaluations(Evaluations=[evaluation], ResultToken=event["resultToken"]) # type: ignore
150+
151+
LOGGER.info("Compliance evaluation complete.")

0 commit comments

Comments
 (0)