awslabs.well-architected-security-mcp-server 0.1.1__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.
- awslabs/well_architected_security_mcp_server/__init__.py +17 -0
- awslabs/well_architected_security_mcp_server/consts.py +113 -0
- awslabs/well_architected_security_mcp_server/server.py +1174 -0
- awslabs/well_architected_security_mcp_server/util/__init__.py +42 -0
- awslabs/well_architected_security_mcp_server/util/network_security.py +1251 -0
- awslabs/well_architected_security_mcp_server/util/prompt_utils.py +173 -0
- awslabs/well_architected_security_mcp_server/util/resource_utils.py +109 -0
- awslabs/well_architected_security_mcp_server/util/security_services.py +1618 -0
- awslabs/well_architected_security_mcp_server/util/storage_security.py +1126 -0
- awslabs_well_architected_security_mcp_server-0.1.1.dist-info/METADATA +258 -0
- awslabs_well_architected_security_mcp_server-0.1.1.dist-info/RECORD +13 -0
- awslabs_well_architected_security_mcp_server-0.1.1.dist-info/WHEEL +4 -0
- awslabs_well_architected_security_mcp_server-0.1.1.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1,1174 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""AWS Well-Architected Security Assessment Tool MCP Server"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import datetime
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from typing import Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
import boto3
|
|
24
|
+
from botocore.config import Config
|
|
25
|
+
from loguru import logger
|
|
26
|
+
from mcp.server.fastmcp import Context, FastMCP
|
|
27
|
+
from pydantic import Field
|
|
28
|
+
|
|
29
|
+
from awslabs.well_architected_security_mcp_server import __version__
|
|
30
|
+
from awslabs.well_architected_security_mcp_server.consts import INSTRUCTIONS
|
|
31
|
+
from awslabs.well_architected_security_mcp_server.util.network_security import (
|
|
32
|
+
check_network_security,
|
|
33
|
+
)
|
|
34
|
+
from awslabs.well_architected_security_mcp_server.util.resource_utils import (
|
|
35
|
+
list_services_in_region,
|
|
36
|
+
)
|
|
37
|
+
from awslabs.well_architected_security_mcp_server.util.security_services import (
|
|
38
|
+
check_access_analyzer,
|
|
39
|
+
check_guard_duty,
|
|
40
|
+
check_inspector,
|
|
41
|
+
check_macie,
|
|
42
|
+
check_security_hub,
|
|
43
|
+
check_trusted_advisor,
|
|
44
|
+
get_access_analyzer_findings,
|
|
45
|
+
get_guardduty_findings,
|
|
46
|
+
get_inspector_findings,
|
|
47
|
+
get_macie_findings,
|
|
48
|
+
get_securityhub_findings,
|
|
49
|
+
get_trusted_advisor_findings,
|
|
50
|
+
)
|
|
51
|
+
from awslabs.well_architected_security_mcp_server.util.storage_security import (
|
|
52
|
+
check_storage_encryption,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# User agent configuration for AWS API calls
|
|
56
|
+
USER_AGENT_CONFIG = Config(
|
|
57
|
+
user_agent_extra=f"awslabs/mcp/well-architected-security-mcp-server/{__version__}"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Set up AWS region and profile from environment variables
|
|
61
|
+
AWS_REGION = os.environ.get("AWS_REGION", "us-east-1")
|
|
62
|
+
AWS_PROFILE = os.environ.get("AWS_PROFILE", "default")
|
|
63
|
+
|
|
64
|
+
# Remove default logger and add custom configuration
|
|
65
|
+
logger.remove()
|
|
66
|
+
logger.add(sys.stderr, level=os.getenv("FASTMCP_LOG_LEVEL", "DEBUG"))
|
|
67
|
+
|
|
68
|
+
# Initialize MCP Server
|
|
69
|
+
mcp = FastMCP(
|
|
70
|
+
"well-architected-security-mcp-server",
|
|
71
|
+
instructions=INSTRUCTIONS,
|
|
72
|
+
dependencies=[
|
|
73
|
+
"boto3",
|
|
74
|
+
"requests",
|
|
75
|
+
"beautifulsoup4",
|
|
76
|
+
"pydantic",
|
|
77
|
+
"loguru",
|
|
78
|
+
],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Global shared components
|
|
82
|
+
security_pattern_catalog = None
|
|
83
|
+
rule_catalog = None
|
|
84
|
+
|
|
85
|
+
# Define Field singleton variables for parameter defaults
|
|
86
|
+
FIELD_AWS_REGION = Field(
|
|
87
|
+
AWS_REGION, description="AWS region to check for security services status"
|
|
88
|
+
)
|
|
89
|
+
FIELD_AWS_PROFILE = Field(
|
|
90
|
+
AWS_PROFILE,
|
|
91
|
+
description="Optional AWS profile to use (defaults to AWS_PROFILE environment variable or 'default')",
|
|
92
|
+
)
|
|
93
|
+
FIELD_STORE_IN_CONTEXT_TRUE = Field(
|
|
94
|
+
True, description="Whether to store results in context for access by other tools"
|
|
95
|
+
)
|
|
96
|
+
FIELD_DEBUG_TRUE = Field(
|
|
97
|
+
True, description="Whether to include detailed debug information in the response"
|
|
98
|
+
)
|
|
99
|
+
FIELD_SECURITY_SERVICES = Field(
|
|
100
|
+
["guardduty", "inspector", "accessanalyzer", "securityhub", "trustedadvisor", "macie"],
|
|
101
|
+
description="List of security services to check. Options: guardduty, inspector, accessanalyzer, securityhub, trustedadvisor, macie",
|
|
102
|
+
)
|
|
103
|
+
FIELD_ACCOUNT_ID = Field(
|
|
104
|
+
None, description="Optional AWS account ID (defaults to caller's account)"
|
|
105
|
+
)
|
|
106
|
+
FIELD_MAX_FINDINGS = Field(100, description="Maximum number of findings to retrieve")
|
|
107
|
+
FIELD_SEVERITY_FILTER = Field(
|
|
108
|
+
None,
|
|
109
|
+
description="Optional severity filter (e.g., 'HIGH', 'CRITICAL', or for Trusted Advisor: 'ERROR', 'WARNING')",
|
|
110
|
+
)
|
|
111
|
+
FIELD_CHECK_ENABLED = Field(
|
|
112
|
+
True, description="Whether to check if service is enabled before retrieving findings"
|
|
113
|
+
)
|
|
114
|
+
FIELD_DETAILED_FALSE = Field(
|
|
115
|
+
False, description="Whether to return the full details of the stored security services data"
|
|
116
|
+
)
|
|
117
|
+
FIELD_STORAGE_SERVICES = Field(
|
|
118
|
+
["s3", "ebs", "rds", "dynamodb", "efs", "elasticache"],
|
|
119
|
+
description="List of storage services to check. Options: s3, ebs, rds, dynamodb, efs, elasticache",
|
|
120
|
+
)
|
|
121
|
+
FIELD_INCLUDE_UNENCRYPTED_ONLY = Field(
|
|
122
|
+
False, description="Whether to include only unencrypted resources in the results"
|
|
123
|
+
)
|
|
124
|
+
FIELD_SERVICE_FILTER = Field(
|
|
125
|
+
None, description="Optional filter to limit results to a specific service (e.g., 's3', 'ec2')"
|
|
126
|
+
)
|
|
127
|
+
FIELD_NETWORK_SERVICES = Field(
|
|
128
|
+
["elb", "vpc", "apigateway", "cloudfront"],
|
|
129
|
+
description="List of network services to check. Options: elb, vpc, apigateway, cloudfront",
|
|
130
|
+
)
|
|
131
|
+
FIELD_INCLUDE_NON_COMPLIANT_ONLY = Field(
|
|
132
|
+
False, description="Whether to include only non-compliant resources in the results"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Global context storage for sharing data between tool calls
|
|
136
|
+
context_storage = {}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@mcp.tool(name="CheckSecurityServices")
|
|
140
|
+
async def check_security_services(
|
|
141
|
+
ctx: Context,
|
|
142
|
+
region: str = FIELD_AWS_REGION,
|
|
143
|
+
services: List[str] = FIELD_SECURITY_SERVICES,
|
|
144
|
+
account_id: Optional[str] = FIELD_ACCOUNT_ID,
|
|
145
|
+
aws_profile: Optional[str] = FIELD_AWS_PROFILE,
|
|
146
|
+
store_in_context: bool = FIELD_STORE_IN_CONTEXT_TRUE,
|
|
147
|
+
debug: bool = FIELD_DEBUG_TRUE,
|
|
148
|
+
) -> Dict:
|
|
149
|
+
"""Verify if selected AWS security services are enabled in the specified region and account.
|
|
150
|
+
|
|
151
|
+
This consolidated tool checks the status of multiple AWS security services in a single call,
|
|
152
|
+
providing a comprehensive overview of your security posture.
|
|
153
|
+
|
|
154
|
+
## Response format
|
|
155
|
+
Returns a dictionary with:
|
|
156
|
+
- region: The region that was checked
|
|
157
|
+
- services_checked: List of services that were checked
|
|
158
|
+
- all_enabled: Boolean indicating if all specified services are enabled
|
|
159
|
+
- service_statuses: Dictionary with detailed status for each service
|
|
160
|
+
- summary: Summary of security recommendations
|
|
161
|
+
|
|
162
|
+
## AWS permissions required
|
|
163
|
+
- guardduty:ListDetectors, guardduty:GetDetector (if checking GuardDuty)
|
|
164
|
+
- inspector2:GetStatus (if checking Inspector)
|
|
165
|
+
- accessanalyzer:ListAnalyzers (if checking Access Analyzer)
|
|
166
|
+
- securityhub:DescribeHub (if checking Security Hub)
|
|
167
|
+
- support:DescribeTrustedAdvisorChecks (if checking Trusted Advisor)
|
|
168
|
+
"""
|
|
169
|
+
try:
|
|
170
|
+
# Start timestamp for measuring execution time
|
|
171
|
+
start_time = datetime.datetime.now()
|
|
172
|
+
|
|
173
|
+
if debug:
|
|
174
|
+
print(
|
|
175
|
+
f"[DEBUG:CheckSecurityServices] Starting security services check for region: {region}"
|
|
176
|
+
)
|
|
177
|
+
print(f"[DEBUG:CheckSecurityServices] Services to check: {', '.join(services)}")
|
|
178
|
+
print(f"[DEBUG:CheckSecurityServices] Using AWS profile: {aws_profile or 'default'}")
|
|
179
|
+
|
|
180
|
+
# Use the provided AWS profile or default to 'default'
|
|
181
|
+
profile_name = aws_profile or "default"
|
|
182
|
+
|
|
183
|
+
# Create a session using the specified profile
|
|
184
|
+
session = boto3.Session(profile_name=profile_name)
|
|
185
|
+
|
|
186
|
+
# Initialize results
|
|
187
|
+
results = {
|
|
188
|
+
"region": region,
|
|
189
|
+
"services_checked": services,
|
|
190
|
+
"all_enabled": True,
|
|
191
|
+
"service_statuses": {},
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if debug:
|
|
195
|
+
# Add debug info to the results
|
|
196
|
+
results["debug_info"] = {
|
|
197
|
+
"start_time": start_time.isoformat(),
|
|
198
|
+
"aws_profile": profile_name,
|
|
199
|
+
"service_details": {},
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# Check each requested service
|
|
203
|
+
for service_name in services:
|
|
204
|
+
# Process status update
|
|
205
|
+
service_start_time = datetime.datetime.now()
|
|
206
|
+
print(f"Checking {service_name} status in {region}...")
|
|
207
|
+
if debug:
|
|
208
|
+
print(f"[DEBUG:CheckSecurityServices] Starting check for {service_name}")
|
|
209
|
+
|
|
210
|
+
service_result = None
|
|
211
|
+
|
|
212
|
+
# Call the appropriate check function based on service name
|
|
213
|
+
if service_name.lower() == "guardduty":
|
|
214
|
+
service_result = await check_guard_duty(region, session, ctx)
|
|
215
|
+
elif service_name.lower() == "inspector":
|
|
216
|
+
service_result = await check_inspector(region, session, ctx)
|
|
217
|
+
elif service_name.lower() == "accessanalyzer":
|
|
218
|
+
# Call the access analyzer check with additional debugging
|
|
219
|
+
print(
|
|
220
|
+
f"[DEBUG:CheckSecurityServices] Calling check_access_analyzer for region {region}"
|
|
221
|
+
)
|
|
222
|
+
service_result = await check_access_analyzer(region, session, ctx)
|
|
223
|
+
print(
|
|
224
|
+
f"[DEBUG:CheckSecurityServices] check_access_analyzer returned: enabled={service_result.get('enabled', False)}"
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# If service_result says not enabled but analyzers are present, override the enabled flag
|
|
228
|
+
analyzers = service_result.get("analyzers", [])
|
|
229
|
+
if not service_result.get("enabled", False) and analyzers and len(analyzers) > 0:
|
|
230
|
+
print(
|
|
231
|
+
"[DEBUG:CheckSecurityServices] OVERRIDING: Access Analyzer has analyzers but reported as disabled. Setting enabled=True"
|
|
232
|
+
)
|
|
233
|
+
service_result["enabled"] = True
|
|
234
|
+
service_result["message"] = (
|
|
235
|
+
f"IAM Access Analyzer is enabled with {len(analyzers)} analyzer(s)."
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Always log the analyzers we found
|
|
239
|
+
analyzers = service_result.get("analyzers", [])
|
|
240
|
+
if analyzers:
|
|
241
|
+
print(
|
|
242
|
+
f"[DEBUG:CheckSecurityServices] Access Analyzer check found {len(analyzers)} analyzers:"
|
|
243
|
+
)
|
|
244
|
+
for idx, analyzer in enumerate(analyzers):
|
|
245
|
+
print(
|
|
246
|
+
f"[DEBUG:CheckSecurityServices] Analyzer {idx + 1}: name={analyzer.get('name')}, status={analyzer.get('status')}"
|
|
247
|
+
)
|
|
248
|
+
else:
|
|
249
|
+
print("[DEBUG:CheckSecurityServices] Access Analyzer check found no analyzers")
|
|
250
|
+
|
|
251
|
+
elif service_name.lower() == "securityhub":
|
|
252
|
+
service_result = await check_security_hub(region, session, ctx)
|
|
253
|
+
elif service_name.lower() == "trustedadvisor":
|
|
254
|
+
service_result = await check_trusted_advisor(region, session, ctx)
|
|
255
|
+
elif service_name.lower() == "macie":
|
|
256
|
+
service_result = await check_macie(region, session, ctx)
|
|
257
|
+
else:
|
|
258
|
+
# Log warning
|
|
259
|
+
print(f"WARNING: Unknown service: {service_name}. Skipping.")
|
|
260
|
+
continue
|
|
261
|
+
|
|
262
|
+
# Add service result to the output
|
|
263
|
+
results["service_statuses"][service_name] = service_result
|
|
264
|
+
|
|
265
|
+
# Update all_enabled flag
|
|
266
|
+
if service_result and not service_result.get("enabled", False):
|
|
267
|
+
results["all_enabled"] = False
|
|
268
|
+
|
|
269
|
+
# Add debug info for this service if debug is enabled
|
|
270
|
+
if debug:
|
|
271
|
+
service_end_time = datetime.datetime.now()
|
|
272
|
+
service_duration = (service_end_time - service_start_time).total_seconds()
|
|
273
|
+
|
|
274
|
+
if "debug_info" in results and "service_details" in results["debug_info"]:
|
|
275
|
+
results["debug_info"]["service_details"][service_name] = {
|
|
276
|
+
"duration_seconds": service_duration,
|
|
277
|
+
"enabled": service_result.get("enabled", False)
|
|
278
|
+
if service_result
|
|
279
|
+
else False,
|
|
280
|
+
"timestamp": service_end_time.isoformat(),
|
|
281
|
+
"status": "success" if service_result else "error",
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
print(
|
|
285
|
+
f"[DEBUG:CheckSecurityServices] {service_name} check completed in {service_duration:.2f} seconds"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Generate summary based on results
|
|
289
|
+
enabled_services = [
|
|
290
|
+
name
|
|
291
|
+
for name, status in results["service_statuses"].items()
|
|
292
|
+
if status.get("enabled", False)
|
|
293
|
+
]
|
|
294
|
+
disabled_services = [
|
|
295
|
+
name
|
|
296
|
+
for name, status in results["service_statuses"].items()
|
|
297
|
+
if not status.get("enabled", False)
|
|
298
|
+
]
|
|
299
|
+
|
|
300
|
+
summary = []
|
|
301
|
+
if enabled_services:
|
|
302
|
+
summary.append(f"Enabled services: {', '.join(enabled_services)}")
|
|
303
|
+
|
|
304
|
+
if disabled_services:
|
|
305
|
+
summary.append(f"Disabled services: {', '.join(disabled_services)}")
|
|
306
|
+
summary.append("Consider enabling these services to improve your security posture.")
|
|
307
|
+
|
|
308
|
+
results["summary"] = " ".join(summary)
|
|
309
|
+
|
|
310
|
+
# Store results in context if requested
|
|
311
|
+
if store_in_context:
|
|
312
|
+
context_key = f"security_services_{region}"
|
|
313
|
+
context_storage[context_key] = results
|
|
314
|
+
print(f"Stored security services results in context with key: {context_key}")
|
|
315
|
+
|
|
316
|
+
return results
|
|
317
|
+
|
|
318
|
+
except Exception as e:
|
|
319
|
+
# Log error
|
|
320
|
+
print(f"ERROR: Error checking security services: {e}")
|
|
321
|
+
return {
|
|
322
|
+
"region": region,
|
|
323
|
+
"services_checked": services,
|
|
324
|
+
"all_enabled": False,
|
|
325
|
+
"error": str(e),
|
|
326
|
+
"message": "Error checking security services status.",
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@mcp.tool(name="GetSecurityFindings")
|
|
331
|
+
async def get_security_findings(
|
|
332
|
+
ctx: Context,
|
|
333
|
+
region: str = FIELD_AWS_REGION,
|
|
334
|
+
service: str = Field(
|
|
335
|
+
...,
|
|
336
|
+
description="Security service to retrieve findings from ('guardduty', 'securityhub', 'inspector', 'accessanalyzer', 'trustedadvisor', 'macie')",
|
|
337
|
+
),
|
|
338
|
+
max_findings: int = FIELD_MAX_FINDINGS,
|
|
339
|
+
severity_filter: Optional[str] = FIELD_SEVERITY_FILTER,
|
|
340
|
+
aws_profile: Optional[str] = FIELD_AWS_PROFILE,
|
|
341
|
+
check_enabled: bool = FIELD_CHECK_ENABLED,
|
|
342
|
+
) -> Dict:
|
|
343
|
+
"""Retrieve security findings from AWS security services.
|
|
344
|
+
|
|
345
|
+
This tool provides a consolidated interface to retrieve findings from various AWS security
|
|
346
|
+
services, including GuardDuty, Security Hub, Inspector, IAM Access Analyzer, and Trusted Advisor.
|
|
347
|
+
|
|
348
|
+
It first checks if the specified security service is enabled in the region (using data from
|
|
349
|
+
a previous CheckSecurityServices call) and only retrieves findings if the service is enabled.
|
|
350
|
+
|
|
351
|
+
## Response format
|
|
352
|
+
Returns a dictionary with:
|
|
353
|
+
- service: The security service findings were retrieved from
|
|
354
|
+
- enabled: Whether the service is enabled in the specified region
|
|
355
|
+
- findings: List of findings from the service (if service is enabled)
|
|
356
|
+
- summary: Summary statistics about the findings (if service is enabled)
|
|
357
|
+
- message: Status message or error information
|
|
358
|
+
|
|
359
|
+
## AWS permissions required
|
|
360
|
+
- Read permissions for the specified security service
|
|
361
|
+
|
|
362
|
+
## Note
|
|
363
|
+
For optimal performance, run CheckSecurityServices with store_in_context=True
|
|
364
|
+
before using this tool. Otherwise, it will need to check if the service is enabled first.
|
|
365
|
+
"""
|
|
366
|
+
try:
|
|
367
|
+
# Normalize service name
|
|
368
|
+
service_name = service.lower()
|
|
369
|
+
|
|
370
|
+
# Check if service is supported
|
|
371
|
+
if service_name not in [
|
|
372
|
+
"guardduty",
|
|
373
|
+
"securityhub",
|
|
374
|
+
"inspector",
|
|
375
|
+
"accessanalyzer",
|
|
376
|
+
"trustedadvisor",
|
|
377
|
+
"macie",
|
|
378
|
+
]:
|
|
379
|
+
raise ValueError(
|
|
380
|
+
f"Unsupported security service: {service}. "
|
|
381
|
+
+ "Supported services are: guardduty, securityhub, inspector, accessanalyzer, trustedadvisor, macie"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# Get context key for security services data
|
|
385
|
+
context_key = f"security_services_{region}"
|
|
386
|
+
service_status = None
|
|
387
|
+
|
|
388
|
+
# First check if we need to verify service is enabled
|
|
389
|
+
if check_enabled:
|
|
390
|
+
# Check if security services data is available in context
|
|
391
|
+
if context_key in context_storage:
|
|
392
|
+
print(f"Using stored security services data for region: {region}")
|
|
393
|
+
security_data = context_storage[context_key]
|
|
394
|
+
|
|
395
|
+
# Check if the requested service is in the stored data
|
|
396
|
+
service_statuses = security_data.get("service_statuses", {})
|
|
397
|
+
if service_name in service_statuses:
|
|
398
|
+
service_status = service_statuses[service_name]
|
|
399
|
+
|
|
400
|
+
# Check if service is enabled
|
|
401
|
+
if not service_status.get("enabled", False):
|
|
402
|
+
return {
|
|
403
|
+
"service": service_name,
|
|
404
|
+
"enabled": False,
|
|
405
|
+
"message": f"{service_name} is not enabled in region {region}. Please enable it before retrieving findings.",
|
|
406
|
+
"setup_instructions": service_status.get(
|
|
407
|
+
"setup_instructions", "No setup instructions available."
|
|
408
|
+
),
|
|
409
|
+
}
|
|
410
|
+
else:
|
|
411
|
+
print(
|
|
412
|
+
f"Service {service_name} not found in stored security services data. Will check directly."
|
|
413
|
+
)
|
|
414
|
+
else:
|
|
415
|
+
print(
|
|
416
|
+
f"No stored security services data found for region: {region}. Will check service status directly."
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
# Use the provided AWS profile or default to 'default'
|
|
420
|
+
profile_name = aws_profile or "default"
|
|
421
|
+
|
|
422
|
+
# Create a session using the specified profile
|
|
423
|
+
session = boto3.Session(profile_name=profile_name)
|
|
424
|
+
|
|
425
|
+
# Prepare filter criteria based on severity
|
|
426
|
+
filter_criteria = None
|
|
427
|
+
if severity_filter:
|
|
428
|
+
if service_name == "guardduty":
|
|
429
|
+
# GuardDuty uses numeric severity levels
|
|
430
|
+
severity_mapping = {
|
|
431
|
+
"LOW": ["1", "2", "3"],
|
|
432
|
+
"MEDIUM": ["4", "5", "6"],
|
|
433
|
+
"HIGH": ["7", "8"],
|
|
434
|
+
"CRITICAL": ["8"],
|
|
435
|
+
}
|
|
436
|
+
if severity_filter.upper() in severity_mapping:
|
|
437
|
+
filter_criteria = {
|
|
438
|
+
"Criterion": {
|
|
439
|
+
"severity": {"Eq": severity_mapping[severity_filter.upper()]}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
elif service_name == "securityhub":
|
|
443
|
+
filter_criteria = {
|
|
444
|
+
"SeverityLabel": [{"Comparison": "EQUALS", "Value": severity_filter.upper()}]
|
|
445
|
+
}
|
|
446
|
+
elif service_name == "inspector":
|
|
447
|
+
filter_criteria = {
|
|
448
|
+
"severities": [{"comparison": "EQUALS", "value": severity_filter.upper()}]
|
|
449
|
+
}
|
|
450
|
+
elif service_name == "trustedadvisor":
|
|
451
|
+
# For Trusted Advisor, severity maps to status (error, warning, ok)
|
|
452
|
+
status_filter = [severity_filter.lower()]
|
|
453
|
+
|
|
454
|
+
# Initialize result with default values
|
|
455
|
+
result = {
|
|
456
|
+
"service": service_name,
|
|
457
|
+
"enabled": False,
|
|
458
|
+
"message": f"Error retrieving {service_name} findings",
|
|
459
|
+
"findings": [],
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
# Call appropriate service function based on service parameter
|
|
463
|
+
if service_name == "guardduty":
|
|
464
|
+
print(f"Retrieving GuardDuty findings from {region}...")
|
|
465
|
+
result = await get_guardduty_findings(
|
|
466
|
+
region, session, ctx, max_findings, filter_criteria
|
|
467
|
+
)
|
|
468
|
+
elif service_name == "securityhub":
|
|
469
|
+
print(f"Retrieving Security Hub findings from {region}...")
|
|
470
|
+
result = await get_securityhub_findings(
|
|
471
|
+
region, session, ctx, max_findings, filter_criteria
|
|
472
|
+
)
|
|
473
|
+
elif service_name == "inspector":
|
|
474
|
+
print(f"Retrieving Inspector findings from {region}...")
|
|
475
|
+
result = await get_inspector_findings(
|
|
476
|
+
region, session, ctx, max_findings, filter_criteria
|
|
477
|
+
)
|
|
478
|
+
elif service_name == "accessanalyzer":
|
|
479
|
+
print(f"Retrieving IAM Access Analyzer findings from {region}...")
|
|
480
|
+
result = await get_access_analyzer_findings(region, session, ctx)
|
|
481
|
+
elif service_name == "trustedadvisor":
|
|
482
|
+
print("Retrieving Trusted Advisor security checks with Error/Warning status...")
|
|
483
|
+
# For Trusted Advisor, we'll focus on security category checks
|
|
484
|
+
# Use the severity filter if provided, otherwise default to error and warning
|
|
485
|
+
if severity_filter:
|
|
486
|
+
status_filter = [severity_filter.lower()]
|
|
487
|
+
print(f"Filtering Trusted Advisor checks by status: {status_filter}")
|
|
488
|
+
else:
|
|
489
|
+
status_filter = ["error", "warning"]
|
|
490
|
+
print(f"Using default status filter for Trusted Advisor: {status_filter}")
|
|
491
|
+
result = await get_trusted_advisor_findings(
|
|
492
|
+
region,
|
|
493
|
+
session,
|
|
494
|
+
ctx,
|
|
495
|
+
max_findings=max_findings,
|
|
496
|
+
status_filter=status_filter,
|
|
497
|
+
category_filter="security",
|
|
498
|
+
)
|
|
499
|
+
elif service_name == "macie":
|
|
500
|
+
print(f"Retrieving Macie findings from {region}...")
|
|
501
|
+
result = await get_macie_findings(region, session, ctx, max_findings, filter_criteria)
|
|
502
|
+
|
|
503
|
+
# Add service info to result
|
|
504
|
+
result["service"] = service_name
|
|
505
|
+
|
|
506
|
+
# If the result indicates the service isn't enabled, store this information
|
|
507
|
+
if not result.get("enabled", True) and context_key in context_storage:
|
|
508
|
+
security_data = context_storage[context_key]
|
|
509
|
+
service_statuses = security_data.get("service_statuses", {})
|
|
510
|
+
if service_name not in service_statuses:
|
|
511
|
+
service_statuses[service_name] = {"enabled": False}
|
|
512
|
+
print(f"Updated context with status for {service_name}: not enabled")
|
|
513
|
+
|
|
514
|
+
return result
|
|
515
|
+
|
|
516
|
+
except Exception as e:
|
|
517
|
+
# Log error
|
|
518
|
+
print(f"ERROR: Error retrieving {service} findings: {e}")
|
|
519
|
+
raise e
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
@mcp.tool(name="GetStoredSecurityContext")
|
|
523
|
+
async def get_stored_security_context(
|
|
524
|
+
ctx: Context,
|
|
525
|
+
region: str = FIELD_AWS_REGION,
|
|
526
|
+
detailed: bool = FIELD_DETAILED_FALSE,
|
|
527
|
+
) -> Dict:
|
|
528
|
+
"""Retrieve security services data that was stored in context from a previous CheckSecurityServices call.
|
|
529
|
+
|
|
530
|
+
This tool allows you to access security service status data stored by the CheckSecurityServices tool
|
|
531
|
+
without making additional AWS API calls. This is useful for workflows where you need to reference
|
|
532
|
+
the security services status in subsequent steps.
|
|
533
|
+
|
|
534
|
+
## Response format
|
|
535
|
+
Returns a dictionary with:
|
|
536
|
+
- region: The region the data was stored for
|
|
537
|
+
- available: Boolean indicating if data is available for the requested region
|
|
538
|
+
- data: The stored security services data (if available and detailed=True)
|
|
539
|
+
- summary: A summary of the stored data (if available)
|
|
540
|
+
- timestamp: When the data was stored (if available)
|
|
541
|
+
|
|
542
|
+
## Note
|
|
543
|
+
This tool requires that CheckSecurityServices was previously called with store_in_context=True
|
|
544
|
+
for the requested region.
|
|
545
|
+
"""
|
|
546
|
+
context_key = f"security_services_{region}"
|
|
547
|
+
|
|
548
|
+
if context_key not in context_storage:
|
|
549
|
+
print(f"No stored security services data found for region: {region}")
|
|
550
|
+
return {
|
|
551
|
+
"region": region,
|
|
552
|
+
"available": False,
|
|
553
|
+
"message": f"No security services data has been stored for region {region}. Call CheckSecurityServices with store_in_context=True first.",
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
stored_data = context_storage[context_key]
|
|
557
|
+
|
|
558
|
+
# Prepare response
|
|
559
|
+
response = {
|
|
560
|
+
"region": region,
|
|
561
|
+
"available": True,
|
|
562
|
+
"summary": stored_data.get("summary", "No summary available"),
|
|
563
|
+
"all_enabled": stored_data.get("all_enabled", False),
|
|
564
|
+
"services_checked": stored_data.get("services_checked", []),
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
# Include full data if requested
|
|
568
|
+
if detailed:
|
|
569
|
+
response["data"] = stored_data
|
|
570
|
+
|
|
571
|
+
print(f"Retrieved stored security services data for region: {region}")
|
|
572
|
+
return response
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
@mcp.tool(name="CheckStorageEncryption")
|
|
576
|
+
async def check_storage_encryption_tool(
|
|
577
|
+
ctx: Context,
|
|
578
|
+
region: str = FIELD_AWS_REGION,
|
|
579
|
+
services: List[str] = FIELD_STORAGE_SERVICES,
|
|
580
|
+
include_unencrypted_only: bool = FIELD_INCLUDE_UNENCRYPTED_ONLY,
|
|
581
|
+
aws_profile: Optional[str] = FIELD_AWS_PROFILE,
|
|
582
|
+
store_in_context: bool = FIELD_STORE_IN_CONTEXT_TRUE,
|
|
583
|
+
) -> Dict:
|
|
584
|
+
"""Check if AWS storage resources have encryption enabled.
|
|
585
|
+
|
|
586
|
+
This tool identifies storage resources using Resource Explorer and checks if they
|
|
587
|
+
are properly configured for data protection at rest according to AWS Well-Architected
|
|
588
|
+
Framework Security Pillar best practices.
|
|
589
|
+
|
|
590
|
+
## Response format
|
|
591
|
+
Returns a dictionary with:
|
|
592
|
+
- region: The region that was checked
|
|
593
|
+
- resources_checked: Total number of storage resources checked
|
|
594
|
+
- compliant_resources: Number of resources with proper encryption
|
|
595
|
+
- non_compliant_resources: Number of resources without proper encryption
|
|
596
|
+
- compliance_by_service: Breakdown of compliance by service type
|
|
597
|
+
- resource_details: Details about each resource checked
|
|
598
|
+
- recommendations: Recommendations for improving data protection at rest
|
|
599
|
+
|
|
600
|
+
## AWS permissions required
|
|
601
|
+
- resource-explorer-2:ListResources
|
|
602
|
+
- Read permissions for each storage service being analyzed (s3:GetEncryptionConfiguration, etc.)
|
|
603
|
+
"""
|
|
604
|
+
try:
|
|
605
|
+
print(f"Starting storage encryption check for region: {region}")
|
|
606
|
+
print(f"Services to check: {', '.join(services)}")
|
|
607
|
+
print(f"Using AWS profile: {aws_profile or 'default'}")
|
|
608
|
+
|
|
609
|
+
# Use the provided AWS profile or default to 'default'
|
|
610
|
+
profile_name = aws_profile or "default"
|
|
611
|
+
|
|
612
|
+
# Create a session using the specified profile
|
|
613
|
+
session = boto3.Session(profile_name=profile_name)
|
|
614
|
+
|
|
615
|
+
# Call the storage security utility function
|
|
616
|
+
results = await check_storage_encryption(
|
|
617
|
+
region, services, session, ctx, include_unencrypted_only
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
# Store results in context if requested
|
|
621
|
+
if store_in_context:
|
|
622
|
+
context_key = f"storage_encryption_{region}"
|
|
623
|
+
context_storage[context_key] = results
|
|
624
|
+
return results
|
|
625
|
+
|
|
626
|
+
except Exception as e:
|
|
627
|
+
# Log error
|
|
628
|
+
print(f"ERROR: Error checking storage encryption: {e}")
|
|
629
|
+
return {
|
|
630
|
+
"region": region,
|
|
631
|
+
"services_checked": services,
|
|
632
|
+
"error": str(e),
|
|
633
|
+
"message": "Error checking storage encryption status.",
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
@mcp.tool(name="ListServicesInRegion")
|
|
638
|
+
async def list_services_in_region_tool(
|
|
639
|
+
ctx: Context,
|
|
640
|
+
region: str = FIELD_AWS_REGION,
|
|
641
|
+
aws_profile: Optional[str] = FIELD_AWS_PROFILE,
|
|
642
|
+
store_in_context: bool = FIELD_STORE_IN_CONTEXT_TRUE,
|
|
643
|
+
) -> Dict:
|
|
644
|
+
"""List all AWS services being used in a specific region.
|
|
645
|
+
|
|
646
|
+
This tool identifies which AWS services are actively being used in the specified region
|
|
647
|
+
by discovering resources through AWS Resource Explorer or direct API calls.
|
|
648
|
+
|
|
649
|
+
## Response format
|
|
650
|
+
Returns a dictionary with:
|
|
651
|
+
- region: The region that was checked
|
|
652
|
+
- services: List of AWS services being used in the region
|
|
653
|
+
- service_counts: Dictionary mapping service names to resource counts
|
|
654
|
+
- total_resources: Total number of resources found across all services
|
|
655
|
+
|
|
656
|
+
## AWS permissions required
|
|
657
|
+
- resource-explorer-2:Search (if Resource Explorer is set up)
|
|
658
|
+
- Read permissions for various AWS services
|
|
659
|
+
"""
|
|
660
|
+
print(f"Starting service discovery for region: {region}")
|
|
661
|
+
print(f"Using AWS profile: {aws_profile or 'default'}")
|
|
662
|
+
|
|
663
|
+
# Use the provided AWS profile or default to 'default'
|
|
664
|
+
profile_name = aws_profile or "default"
|
|
665
|
+
|
|
666
|
+
# Create a session using the specified profile
|
|
667
|
+
session = boto3.Session(profile_name=profile_name)
|
|
668
|
+
|
|
669
|
+
# Initialize results with default values
|
|
670
|
+
results = {"region": region, "services": [], "service_counts": {}, "total_resources": 0}
|
|
671
|
+
|
|
672
|
+
try:
|
|
673
|
+
# First try using Resource Explorer method
|
|
674
|
+
print(f"Attempting to discover services using Resource Explorer in {region}...")
|
|
675
|
+
results = await list_services_in_region(region, session, ctx)
|
|
676
|
+
|
|
677
|
+
except Exception as e:
|
|
678
|
+
# If Resource Explorer method fails, log the error and try alternative method
|
|
679
|
+
print(f"Resource Explorer method failed: {e}")
|
|
680
|
+
print("Falling back to alternative service discovery method...")
|
|
681
|
+
|
|
682
|
+
return {
|
|
683
|
+
"region": region,
|
|
684
|
+
"error": f"Discovery methods failed. Primary error: {str(e)}.",
|
|
685
|
+
"message": f"Error listing services in region {region}.",
|
|
686
|
+
"services": [],
|
|
687
|
+
"service_counts": {},
|
|
688
|
+
"total_resources": 0,
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
# Store results in context if requested
|
|
692
|
+
if store_in_context:
|
|
693
|
+
context_key = f"services_in_region_{region}"
|
|
694
|
+
context_storage[context_key] = results
|
|
695
|
+
return results
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
@mcp.tool(name="CheckNetworkSecurity")
|
|
699
|
+
async def check_network_security_tool(
|
|
700
|
+
ctx: Context,
|
|
701
|
+
region: str = FIELD_AWS_REGION,
|
|
702
|
+
services: List[str] = FIELD_NETWORK_SERVICES,
|
|
703
|
+
include_non_compliant_only: bool = FIELD_INCLUDE_NON_COMPLIANT_ONLY,
|
|
704
|
+
aws_profile: Optional[str] = FIELD_AWS_PROFILE,
|
|
705
|
+
store_in_context: bool = FIELD_STORE_IN_CONTEXT_TRUE,
|
|
706
|
+
) -> Dict:
|
|
707
|
+
"""Check if AWS network resources are configured for secure data-in-transit.
|
|
708
|
+
|
|
709
|
+
This tool identifies network resources using Resource Explorer and checks if they
|
|
710
|
+
are properly configured for data protection in transit according to AWS Well-Architected
|
|
711
|
+
Framework Security Pillar best practices.
|
|
712
|
+
|
|
713
|
+
## Response format
|
|
714
|
+
Returns a dictionary with:
|
|
715
|
+
- region: The region that was checked
|
|
716
|
+
- resources_checked: Total number of network resources checked
|
|
717
|
+
- compliant_resources: Number of resources with proper in-transit protection
|
|
718
|
+
- non_compliant_resources: Number of resources without proper in-transit protection
|
|
719
|
+
- compliance_by_service: Breakdown of compliance by service type
|
|
720
|
+
- resource_details: Details about each resource checked
|
|
721
|
+
- recommendations: Recommendations for improving data protection in transit
|
|
722
|
+
|
|
723
|
+
## AWS permissions required
|
|
724
|
+
- resource-explorer-2:ListResources
|
|
725
|
+
- Read permissions for each network service being analyzed (elb:DescribeLoadBalancers, etc.)
|
|
726
|
+
"""
|
|
727
|
+
try:
|
|
728
|
+
print(f"Starting network security check for region: {region}")
|
|
729
|
+
print(f"Services to check: {', '.join(services)}")
|
|
730
|
+
print(f"Using AWS profile: {aws_profile or 'default'}")
|
|
731
|
+
|
|
732
|
+
# Use the provided AWS profile or default to 'default'
|
|
733
|
+
profile_name = aws_profile or "default"
|
|
734
|
+
|
|
735
|
+
# Create a session using the specified profile
|
|
736
|
+
session = boto3.Session(profile_name=profile_name)
|
|
737
|
+
|
|
738
|
+
# Call the network security utility function
|
|
739
|
+
results = await check_network_security(
|
|
740
|
+
region, services, session, ctx, include_non_compliant_only
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
# Store results in context if requested
|
|
744
|
+
if store_in_context:
|
|
745
|
+
context_key = f"network_security_{region}"
|
|
746
|
+
context_storage[context_key] = results
|
|
747
|
+
return results
|
|
748
|
+
|
|
749
|
+
except Exception as e:
|
|
750
|
+
# Log error
|
|
751
|
+
print(f"ERROR: Error checking network security: {e}")
|
|
752
|
+
return {
|
|
753
|
+
"region": region,
|
|
754
|
+
"services_checked": services,
|
|
755
|
+
"error": str(e),
|
|
756
|
+
"message": "Error checking network security status.",
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
@mcp.prompt(name="wa-sec-check-findings")
|
|
761
|
+
async def security_assessment_precheck(ctx: Context) -> str:
|
|
762
|
+
"""Provides guidance on using CheckSecurityServices and GetSecurityFindings tools in sequence
|
|
763
|
+
for a comprehensive AWS security assessment.
|
|
764
|
+
|
|
765
|
+
This prompt explains the recommended workflow for assessing AWS security services and findings:
|
|
766
|
+
1. First, check which security services are enabled using CheckSecurityServices
|
|
767
|
+
2. Then, retrieve findings from the enabled services using GetSecurityFindings
|
|
768
|
+
|
|
769
|
+
Following this sequence ensures efficient API usage and provides a structured approach to security assessment.
|
|
770
|
+
"""
|
|
771
|
+
return """
|
|
772
|
+
# AWS Security Assessment Workflow Guide
|
|
773
|
+
|
|
774
|
+
This guide will help you assess your AWS security posture by checking which security services are enabled and retrieving findings from those services.
|
|
775
|
+
|
|
776
|
+
## Step 1: Check Security Services Status
|
|
777
|
+
|
|
778
|
+
First, use the `CheckSecurityServices` tool to determine which AWS security services are enabled in your account:
|
|
779
|
+
|
|
780
|
+
```python
|
|
781
|
+
result = await use_mcp_tool(
|
|
782
|
+
server_name="well-architected-security-mcp-server",
|
|
783
|
+
tool_name="CheckSecurityServices",
|
|
784
|
+
arguments={
|
|
785
|
+
"region": "us-east-1", # Specify your AWS region
|
|
786
|
+
"services": ["guardduty", "inspector", "accessanalyzer", "securityhub", "trustedadvisor"],
|
|
787
|
+
"aws_profile": "default", # Optional: specify your AWS profile
|
|
788
|
+
"store_in_context": True # Important: store results for later use
|
|
789
|
+
}
|
|
790
|
+
)
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
This will check the status of each security service and store the results in context for later use.
|
|
794
|
+
|
|
795
|
+
## Step 2: Analyze the Results
|
|
796
|
+
|
|
797
|
+
Review the results to see which services are enabled:
|
|
798
|
+
|
|
799
|
+
```python
|
|
800
|
+
enabled_services = []
|
|
801
|
+
for service, status in result['service_statuses'].items():
|
|
802
|
+
if status.get('enabled', False):
|
|
803
|
+
enabled_services.append(service)
|
|
804
|
+
print(f"✅ {service} is enabled")
|
|
805
|
+
else:
|
|
806
|
+
print(f"❌ {service} is not enabled")
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
## Step 3: Retrieve Findings from Enabled Services
|
|
810
|
+
|
|
811
|
+
For each enabled service, use the `GetSecurityFindings` tool to retrieve findings:
|
|
812
|
+
|
|
813
|
+
```python
|
|
814
|
+
for service in enabled_services:
|
|
815
|
+
findings = await use_mcp_tool(
|
|
816
|
+
server_name="well-architected-security-mcp-server",
|
|
817
|
+
tool_name="GetSecurityFindings",
|
|
818
|
+
arguments={
|
|
819
|
+
"region": "us-east-1", # Use the same region as in Step 1
|
|
820
|
+
"service": service,
|
|
821
|
+
"max_findings": 100, # Adjust as needed
|
|
822
|
+
"severity_filter": "HIGH", # Optional: filter by severity
|
|
823
|
+
"check_enabled": True # Verify service is enabled before retrieving findings
|
|
824
|
+
}
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
# Process the findings
|
|
828
|
+
if findings.get('findings'):
|
|
829
|
+
print(f"Found {len(findings['findings'])} {service} findings")
|
|
830
|
+
# Analyze findings here
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
## Step 4: Summarize Security Posture
|
|
834
|
+
|
|
835
|
+
After retrieving findings from all enabled services, summarize the security posture:
|
|
836
|
+
|
|
837
|
+
```python
|
|
838
|
+
total_findings = 0
|
|
839
|
+
findings_by_service = {}
|
|
840
|
+
|
|
841
|
+
for service in enabled_services:
|
|
842
|
+
# Get findings count for each service
|
|
843
|
+
# Implement your summary logic here
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
## Best Practices
|
|
847
|
+
|
|
848
|
+
1. Always run `CheckSecurityServices` first with `store_in_context=True`
|
|
849
|
+
2. Use `GetSecurityFindings` only for services that are enabled
|
|
850
|
+
3. Consider filtering findings by severity to focus on high-risk issues first
|
|
851
|
+
4. For large environments, process findings in batches
|
|
852
|
+
|
|
853
|
+
By following this workflow, you'll efficiently assess your AWS security posture and identify potential security issues.
|
|
854
|
+
"""
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
@mcp.prompt(name="wa-sec-check-storage")
|
|
858
|
+
async def check_storage_security_prompt(ctx: Context) -> str:
|
|
859
|
+
"""Provides guidance on checking AWS storage resources for proper encryption and security configuration.
|
|
860
|
+
|
|
861
|
+
This prompt explains the recommended workflow for assessing storage security:
|
|
862
|
+
1. First, identify available storage services in the target region
|
|
863
|
+
2. Then, check if these storage resources have encryption enabled
|
|
864
|
+
3. Finally, analyze the results and implement recommended remediation steps
|
|
865
|
+
|
|
866
|
+
This approach helps ensure data protection at rest according to AWS Well-Architected Framework
|
|
867
|
+
Security Pillar best practices.
|
|
868
|
+
"""
|
|
869
|
+
return """
|
|
870
|
+
# AWS Storage Security Assessment Guide
|
|
871
|
+
|
|
872
|
+
This guide will help you assess the security of your AWS storage resources by checking for proper encryption and security configurations.
|
|
873
|
+
|
|
874
|
+
## Step 1: Identify Available Storage Services
|
|
875
|
+
|
|
876
|
+
First, determine which storage services are available in your target region:
|
|
877
|
+
|
|
878
|
+
```python
|
|
879
|
+
# Option 1: List all services in the region
|
|
880
|
+
services_result = await use_mcp_tool(
|
|
881
|
+
server_name="well-architected-security-mcp-server",
|
|
882
|
+
tool_name="ListServicesInRegion",
|
|
883
|
+
arguments={
|
|
884
|
+
"region": "us-east-1", # Specify your AWS region
|
|
885
|
+
"aws_profile": "default", # Optional: specify your AWS profile
|
|
886
|
+
"store_in_context": True # Store results for later use
|
|
887
|
+
}
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
# Option 2: List resource types (alternative approach)
|
|
891
|
+
resource_types = await use_mcp_tool(
|
|
892
|
+
server_name="well-architected-security-mcp-server",
|
|
893
|
+
tool_name="ListResourceTypes",
|
|
894
|
+
arguments={
|
|
895
|
+
"region": "us-east-1", # Specify your AWS region
|
|
896
|
+
"aws_profile": "default", # Optional: specify your AWS profile
|
|
897
|
+
"store_in_context": True # Store results for later use
|
|
898
|
+
}
|
|
899
|
+
)
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
## Step 2: Filter for Storage Services
|
|
903
|
+
|
|
904
|
+
Next, filter the results to focus on storage services:
|
|
905
|
+
|
|
906
|
+
```python
|
|
907
|
+
# Define storage services to check
|
|
908
|
+
storage_services = ['s3', 'ebs', 'rds', 'dynamodb', 'efs', 'elasticache']
|
|
909
|
+
|
|
910
|
+
# Filter available services to include only storage services
|
|
911
|
+
available_storage_services = []
|
|
912
|
+
|
|
913
|
+
# If using ListServicesInRegion result
|
|
914
|
+
if 'services' in services_result:
|
|
915
|
+
available_storage_services = [s for s in services_result['services'] if s in storage_services]
|
|
916
|
+
|
|
917
|
+
# If using ListResourceTypes result
|
|
918
|
+
if 'storage_services' in resource_types:
|
|
919
|
+
available_storage_services = resource_types['storage_services']
|
|
920
|
+
|
|
921
|
+
print(f"Available storage services: {', '.join(available_storage_services)}")
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
## Step 3: Check Storage Encryption
|
|
925
|
+
|
|
926
|
+
Now, check if your storage resources have encryption enabled:
|
|
927
|
+
|
|
928
|
+
```python
|
|
929
|
+
encryption_result = await use_mcp_tool(
|
|
930
|
+
server_name="well-architected-security-mcp-server",
|
|
931
|
+
tool_name="CheckStorageEncryption",
|
|
932
|
+
arguments={
|
|
933
|
+
"region": "us-east-1", # Specify your AWS region
|
|
934
|
+
"services": available_storage_services, # Use the filtered list from Step 2
|
|
935
|
+
"include_unencrypted_only": False, # Set to True to focus only on unencrypted resources
|
|
936
|
+
"aws_profile": "default", # Optional: specify your AWS profile
|
|
937
|
+
"store_in_context": True # Store results for later use
|
|
938
|
+
}
|
|
939
|
+
)
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
## Step 4: Analyze the Results
|
|
943
|
+
|
|
944
|
+
Review the encryption check results:
|
|
945
|
+
|
|
946
|
+
```python
|
|
947
|
+
# Get overall compliance statistics
|
|
948
|
+
total_resources = encryption_result['resources_checked']
|
|
949
|
+
compliant_resources = encryption_result['compliant_resources']
|
|
950
|
+
non_compliant_resources = encryption_result['non_compliant_resources']
|
|
951
|
+
|
|
952
|
+
print(f"Total resources checked: {total_resources}")
|
|
953
|
+
print(f"Compliant resources: {compliant_resources} ({(compliant_resources/total_resources)*100:.1f}% if total_resources > 0 else 0}%)")
|
|
954
|
+
print(f"Non-compliant resources: {non_compliant_resources} ({(non_compliant_resources/total_resources)*100:.1f}% if total_resources > 0 else 0}%)")
|
|
955
|
+
|
|
956
|
+
# Review compliance by service
|
|
957
|
+
for service, stats in encryption_result['compliance_by_service'].items():
|
|
958
|
+
service_total = stats['resources_checked']
|
|
959
|
+
service_compliant = stats['compliant_resources']
|
|
960
|
+
service_non_compliant = stats['non_compliant_resources']
|
|
961
|
+
|
|
962
|
+
if service_total > 0:
|
|
963
|
+
compliance_rate = (service_compliant / service_total) * 100
|
|
964
|
+
print(f"{service}: {compliance_rate:.1f}% compliant ({service_compliant}/{service_total})")
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
## Step 5: Review Non-Compliant Resources
|
|
968
|
+
|
|
969
|
+
Examine the details of non-compliant resources:
|
|
970
|
+
|
|
971
|
+
```python
|
|
972
|
+
# List all non-compliant resources
|
|
973
|
+
print("\\nNon-compliant resources:")
|
|
974
|
+
for resource in encryption_result['resource_details']:
|
|
975
|
+
if not resource.get('compliant', True):
|
|
976
|
+
print(f"- {resource['type']}: {resource['name']}")
|
|
977
|
+
print(f" Issues: {', '.join(resource['issues'])}")
|
|
978
|
+
print(f" Remediation: {', '.join(resource['remediation'])}")
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
## Step 6: Implement Recommendations
|
|
982
|
+
|
|
983
|
+
Review and implement the recommended remediation steps:
|
|
984
|
+
|
|
985
|
+
```python
|
|
986
|
+
print("\\nRecommendations:")
|
|
987
|
+
for recommendation in encryption_result['recommendations']:
|
|
988
|
+
print(f"- {recommendation}")
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
## Best Practices for Storage Security
|
|
992
|
+
|
|
993
|
+
1. **Enable encryption by default** for all storage services
|
|
994
|
+
2. **Use customer-managed KMS keys** for sensitive data rather than AWS-managed keys
|
|
995
|
+
3. **Implement key rotation policies** for all customer-managed KMS keys
|
|
996
|
+
4. **Block public access** for S3 buckets at the account level
|
|
997
|
+
5. **Enable bucket key** for S3 buckets to reduce KMS API calls and costs
|
|
998
|
+
6. **Audit encryption settings regularly** to ensure continued compliance
|
|
999
|
+
|
|
1000
|
+
By following this workflow, you'll efficiently assess your AWS storage security posture and identify resources that need encryption or security improvements.
|
|
1001
|
+
"""
|
|
1002
|
+
|
|
1003
|
+
|
|
1004
|
+
@mcp.prompt(name="wa-sec-check-network")
|
|
1005
|
+
async def check_network_security_prompt(ctx: Context) -> str:
|
|
1006
|
+
"""Provides guidance on checking AWS network resources for proper in-transit security configuration.
|
|
1007
|
+
|
|
1008
|
+
This prompt explains the recommended workflow for assessing network security:
|
|
1009
|
+
1. First, identify available network services in the target region
|
|
1010
|
+
2. Then, check if these network resources have proper in-transit security measures
|
|
1011
|
+
3. Finally, analyze the results and implement recommended remediation steps
|
|
1012
|
+
|
|
1013
|
+
This approach helps ensure data protection in transit according to AWS Well-Architected Framework
|
|
1014
|
+
Security Pillar best practices.
|
|
1015
|
+
"""
|
|
1016
|
+
return """
|
|
1017
|
+
# AWS Network Security Assessment Guide
|
|
1018
|
+
|
|
1019
|
+
This guide will help you assess the security of your AWS network resources by checking for proper in-transit security configurations.
|
|
1020
|
+
|
|
1021
|
+
## Step 1: Identify Available Network Services
|
|
1022
|
+
|
|
1023
|
+
First, determine which network services are available in your target region:
|
|
1024
|
+
|
|
1025
|
+
```python
|
|
1026
|
+
# Option 1: List all services in the region
|
|
1027
|
+
services_result = await use_mcp_tool(
|
|
1028
|
+
server_name="well-architected-security-mcp-server",
|
|
1029
|
+
tool_name="ListServicesInRegion",
|
|
1030
|
+
arguments={
|
|
1031
|
+
"region": "us-east-1", # Specify your AWS region
|
|
1032
|
+
"aws_profile": "default", # Optional: specify your AWS profile
|
|
1033
|
+
"store_in_context": True # Store results for later use
|
|
1034
|
+
}
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
# Option 2: List resource types (alternative approach)
|
|
1038
|
+
resource_types = await use_mcp_tool(
|
|
1039
|
+
server_name="well-architected-security-mcp-server",
|
|
1040
|
+
tool_name="ListResourceTypes",
|
|
1041
|
+
arguments={
|
|
1042
|
+
"region": "us-east-1", # Specify your AWS region
|
|
1043
|
+
"aws_profile": "default", # Optional: specify your AWS profile
|
|
1044
|
+
"store_in_context": True # Store results for later use
|
|
1045
|
+
}
|
|
1046
|
+
)
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
## Step 2: Filter for Network Services
|
|
1050
|
+
|
|
1051
|
+
Next, filter the results to focus on network services:
|
|
1052
|
+
|
|
1053
|
+
```python
|
|
1054
|
+
# Define network services to check
|
|
1055
|
+
network_services = ['elb', 'vpc', 'apigateway', 'cloudfront']
|
|
1056
|
+
|
|
1057
|
+
# Filter available services to include only network services
|
|
1058
|
+
available_network_services = []
|
|
1059
|
+
|
|
1060
|
+
# If using ListServicesInRegion result
|
|
1061
|
+
if 'services' in services_result:
|
|
1062
|
+
available_network_services = [s for s in services_result['services'] if s in network_services]
|
|
1063
|
+
|
|
1064
|
+
# If using ListResourceTypes result
|
|
1065
|
+
if 'network_services' in resource_types:
|
|
1066
|
+
available_network_services = resource_types['network_services']
|
|
1067
|
+
|
|
1068
|
+
print(f"Available network services: {', '.join(available_network_services)}")
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
## Step 3: Check Network Security
|
|
1072
|
+
|
|
1073
|
+
Now, check if your network resources have proper in-transit security measures:
|
|
1074
|
+
|
|
1075
|
+
```python
|
|
1076
|
+
network_result = await use_mcp_tool(
|
|
1077
|
+
server_name="well-architected-security-mcp-server",
|
|
1078
|
+
tool_name="CheckNetworkSecurity",
|
|
1079
|
+
arguments={
|
|
1080
|
+
"region": "us-east-1", # Specify your AWS region
|
|
1081
|
+
"services": available_network_services, # Use the filtered list from Step 2
|
|
1082
|
+
"include_non_compliant_only": False, # Set to True to focus only on non-compliant resources
|
|
1083
|
+
"aws_profile": "default", # Optional: specify your AWS profile
|
|
1084
|
+
"store_in_context": True # Store results for later use
|
|
1085
|
+
}
|
|
1086
|
+
)
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
## Step 4: Analyze the Results
|
|
1090
|
+
|
|
1091
|
+
Review the network security check results:
|
|
1092
|
+
|
|
1093
|
+
```python
|
|
1094
|
+
# Get overall compliance statistics
|
|
1095
|
+
total_resources = network_result['resources_checked']
|
|
1096
|
+
compliant_resources = network_result['compliant_resources']
|
|
1097
|
+
non_compliant_resources = network_result['non_compliant_resources']
|
|
1098
|
+
|
|
1099
|
+
print(f"Total resources checked: {total_resources}")
|
|
1100
|
+
print(f"Compliant resources: {compliant_resources} ({(compliant_resources/total_resources)*100:.1f}% if total_resources > 0 else 0}%)")
|
|
1101
|
+
print(f"Non-compliant resources: {non_compliant_resources} ({(non_compliant_resources/total_resources)*100:.1f}% if total_resources > 0 else 0}%)")
|
|
1102
|
+
|
|
1103
|
+
# Review compliance by service
|
|
1104
|
+
for service, stats in network_result['compliance_by_service'].items():
|
|
1105
|
+
service_total = stats['resources_checked']
|
|
1106
|
+
service_compliant = stats['compliant_resources']
|
|
1107
|
+
service_non_compliant = stats['non_compliant_resources']
|
|
1108
|
+
|
|
1109
|
+
if service_total > 0:
|
|
1110
|
+
compliance_rate = (service_compliant / service_total) * 100
|
|
1111
|
+
print(f"{service}: {compliance_rate:.1f}% compliant ({service_compliant}/{service_total})")
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
## Step 5: Review Non-Compliant Resources
|
|
1115
|
+
|
|
1116
|
+
Examine the details of non-compliant resources:
|
|
1117
|
+
|
|
1118
|
+
```python
|
|
1119
|
+
# List all non-compliant resources
|
|
1120
|
+
print("\\nNon-compliant resources:")
|
|
1121
|
+
for resource in network_result['resource_details']:
|
|
1122
|
+
if not resource.get('compliant', True):
|
|
1123
|
+
print(f"- {resource['type']}: {resource['name']}")
|
|
1124
|
+
print(f" Issues: {', '.join(resource['issues'])}")
|
|
1125
|
+
print(f" Remediation: {', '.join(resource['remediation'])}")
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
## Step 6: Implement Recommendations
|
|
1129
|
+
|
|
1130
|
+
Review and implement the recommended remediation steps:
|
|
1131
|
+
|
|
1132
|
+
```python
|
|
1133
|
+
print("\\nRecommendations:")
|
|
1134
|
+
for recommendation in network_result['recommendations']:
|
|
1135
|
+
print(f"- {recommendation}")
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
## Best Practices for Network Security
|
|
1139
|
+
|
|
1140
|
+
1. **Use HTTPS/TLS** for all public-facing endpoints
|
|
1141
|
+
2. **Configure security policies** to use modern TLS versions (TLS 1.2 or later)
|
|
1142
|
+
3. **Implement strict security headers** for web applications
|
|
1143
|
+
4. **Use AWS Certificate Manager (ACM)** for managing SSL/TLS certificates
|
|
1144
|
+
5. **Enable VPC Flow Logs** to monitor network traffic
|
|
1145
|
+
6. **Implement network segmentation** using security groups and NACLs
|
|
1146
|
+
7. **Use AWS WAF** to protect web applications from common exploits
|
|
1147
|
+
8. **Regularly audit network security configurations** to ensure continued compliance
|
|
1148
|
+
|
|
1149
|
+
By following this workflow, you'll efficiently assess your AWS network security posture and identify resources that need security improvements for data in transit.
|
|
1150
|
+
"""
|
|
1151
|
+
|
|
1152
|
+
|
|
1153
|
+
def main():
|
|
1154
|
+
"""Run the MCP server with CLI argument support."""
|
|
1155
|
+
parser = argparse.ArgumentParser(description="AWS Security Pillar MCP Server")
|
|
1156
|
+
parser.add_argument("--sse", action="store_true", help="Use SSE transport")
|
|
1157
|
+
parser.add_argument("--port", type=int, default=8888, help="Port to run the server on")
|
|
1158
|
+
|
|
1159
|
+
args = parser.parse_args()
|
|
1160
|
+
|
|
1161
|
+
logger.info("Starting AWS Security Pillar MCP Server")
|
|
1162
|
+
|
|
1163
|
+
# Run server with appropriate transport
|
|
1164
|
+
if args.sse:
|
|
1165
|
+
logger.info(f"Running MCP server with SSE transport on port {args.port}")
|
|
1166
|
+
mcp.settings.port = args.port
|
|
1167
|
+
mcp.run(transport="sse")
|
|
1168
|
+
else:
|
|
1169
|
+
logger.info("Running MCP server with default transport")
|
|
1170
|
+
mcp.run()
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
if __name__ == "__main__":
|
|
1174
|
+
main()
|