compute.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. """
  2. Compute and Storage Service Scanners
  3. Scans EKS Clusters, Lambda Functions, S3 Buckets, and S3 Event Notifications.
  4. Requirements:
  5. - 5.1: Scan compute and storage AWS services using boto3
  6. """
  7. import boto3
  8. from typing import List, Dict, Any
  9. import logging
  10. from app.scanners.base import ResourceData
  11. from app.scanners.utils import retry_with_backoff
  12. logger = logging.getLogger(__name__)
  13. class ComputeServiceScanner:
  14. """Scanner for compute and storage AWS resources"""
  15. @staticmethod
  16. @retry_with_backoff()
  17. def scan_eks_clusters(session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  18. """
  19. Scan EKS Clusters in the specified region.
  20. Attributes (vertical layout - one table per cluster):
  21. Cluster Name, Version, Status, Endpoint, VPC ID
  22. """
  23. resources = []
  24. eks_client = session.client('eks')
  25. try:
  26. # List clusters
  27. paginator = eks_client.get_paginator('list_clusters')
  28. cluster_names = []
  29. for page in paginator.paginate():
  30. cluster_names.extend(page.get('clusters', []))
  31. # Get details for each cluster
  32. for cluster_name in cluster_names:
  33. try:
  34. response = eks_client.describe_cluster(name=cluster_name)
  35. cluster = response.get('cluster', {})
  36. resources.append(ResourceData(
  37. account_id=account_id,
  38. region=region,
  39. service='eks',
  40. resource_type='Cluster',
  41. resource_id=cluster.get('arn', cluster_name),
  42. name=cluster_name,
  43. attributes={
  44. 'Cluster Name': cluster_name,
  45. 'Version': cluster.get('version', ''),
  46. 'Status': cluster.get('status', ''),
  47. 'Endpoint': cluster.get('endpoint', ''),
  48. 'VPC ID': cluster.get('resourcesVpcConfig', {}).get('vpcId', '')
  49. }
  50. ))
  51. except Exception as e:
  52. logger.warning(f"Failed to describe EKS cluster {cluster_name}: {str(e)}")
  53. except Exception as e:
  54. logger.warning(f"Failed to list EKS clusters: {str(e)}")
  55. return resources
  56. @staticmethod
  57. @retry_with_backoff()
  58. def scan_lambda_functions(session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  59. """
  60. Scan Lambda Functions in the specified region.
  61. Attributes (horizontal layout):
  62. Function Name, Runtime, Memory (MB), Timeout (s), Last Modified
  63. """
  64. resources = []
  65. lambda_client = session.client('lambda')
  66. try:
  67. paginator = lambda_client.get_paginator('list_functions')
  68. for page in paginator.paginate():
  69. for func in page.get('Functions', []):
  70. func_name = func.get('FunctionName', '')
  71. resources.append(ResourceData(
  72. account_id=account_id,
  73. region=region,
  74. service='lambda',
  75. resource_type='Function',
  76. resource_id=func.get('FunctionArn', func_name),
  77. name=func_name,
  78. attributes={
  79. 'Function Name': func_name,
  80. 'Runtime': func.get('Runtime', 'N/A'),
  81. 'Memory (MB)': str(func.get('MemorySize', '')),
  82. 'Timeout (s)': str(func.get('Timeout', '')),
  83. 'Last Modified': func.get('LastModified', '')
  84. }
  85. ))
  86. except Exception as e:
  87. logger.warning(f"Failed to scan Lambda functions: {str(e)}")
  88. return resources
  89. @staticmethod
  90. @retry_with_backoff()
  91. def scan_s3_buckets(session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  92. """
  93. Scan S3 Buckets (global service, scanned once).
  94. Attributes (horizontal layout): Region, Bucket Name
  95. """
  96. resources = []
  97. s3_client = session.client('s3')
  98. try:
  99. response = s3_client.list_buckets()
  100. for bucket in response.get('Buckets', []):
  101. bucket_name = bucket.get('Name', '')
  102. # Get bucket location
  103. try:
  104. location_response = s3_client.get_bucket_location(Bucket=bucket_name)
  105. bucket_region = location_response.get('LocationConstraint') or 'us-east-1'
  106. except Exception:
  107. bucket_region = 'unknown'
  108. resources.append(ResourceData(
  109. account_id=account_id,
  110. region='global',
  111. service='s3',
  112. resource_type='Bucket',
  113. resource_id=bucket_name,
  114. name=bucket_name,
  115. attributes={
  116. 'Region': bucket_region,
  117. 'Bucket Name': bucket_name
  118. }
  119. ))
  120. except Exception as e:
  121. logger.warning(f"Failed to scan S3 buckets: {str(e)}")
  122. return resources
  123. @staticmethod
  124. @retry_with_backoff()
  125. def scan_s3_event_notifications(session: boto3.Session, account_id: str, region: str) -> List[ResourceData]:
  126. """
  127. Scan S3 Event Notifications (global service).
  128. Attributes (vertical layout):
  129. Bucket, Name, Event Type, Destination type, Destination
  130. """
  131. resources = []
  132. s3_client = session.client('s3')
  133. try:
  134. # First get all buckets
  135. buckets_response = s3_client.list_buckets()
  136. for bucket in buckets_response.get('Buckets', []):
  137. bucket_name = bucket.get('Name', '')
  138. try:
  139. # Get notification configuration
  140. notif_response = s3_client.get_bucket_notification_configuration(
  141. Bucket=bucket_name
  142. )
  143. # Process Lambda function configurations
  144. for config in notif_response.get('LambdaFunctionConfigurations', []):
  145. config_id = config.get('Id', 'Lambda')
  146. events = config.get('Events', [])
  147. lambda_arn = config.get('LambdaFunctionArn', '')
  148. resources.append(ResourceData(
  149. account_id=account_id,
  150. region='global',
  151. service='s3_event_notification',
  152. resource_type='S3 event notification',
  153. resource_id=f"{bucket_name}/{config_id}",
  154. name=config_id,
  155. attributes={
  156. 'Bucket': bucket_name,
  157. 'Name': config_id,
  158. 'Event Type': ', '.join(events),
  159. 'Destination type': 'Lambda',
  160. 'Destination': lambda_arn.split(':')[-1] if lambda_arn else ''
  161. }
  162. ))
  163. # Process SQS queue configurations
  164. for config in notif_response.get('QueueConfigurations', []):
  165. config_id = config.get('Id', 'SQS')
  166. events = config.get('Events', [])
  167. queue_arn = config.get('QueueArn', '')
  168. resources.append(ResourceData(
  169. account_id=account_id,
  170. region='global',
  171. service='s3_event_notification',
  172. resource_type='S3 event notification',
  173. resource_id=f"{bucket_name}/{config_id}",
  174. name=config_id,
  175. attributes={
  176. 'Bucket': bucket_name,
  177. 'Name': config_id,
  178. 'Event Type': ', '.join(events),
  179. 'Destination type': 'SQS',
  180. 'Destination': queue_arn.split(':')[-1] if queue_arn else ''
  181. }
  182. ))
  183. # Process SNS topic configurations
  184. for config in notif_response.get('TopicConfigurations', []):
  185. config_id = config.get('Id', 'SNS')
  186. events = config.get('Events', [])
  187. topic_arn = config.get('TopicArn', '')
  188. resources.append(ResourceData(
  189. account_id=account_id,
  190. region='global',
  191. service='s3_event_notification',
  192. resource_type='S3 event notification',
  193. resource_id=f"{bucket_name}/{config_id}",
  194. name=config_id,
  195. attributes={
  196. 'Bucket': bucket_name,
  197. 'Name': config_id,
  198. 'Event Type': ', '.join(events),
  199. 'Destination type': 'SNS',
  200. 'Destination': topic_arn.split(':')[-1] if topic_arn else ''
  201. }
  202. ))
  203. except Exception as e:
  204. # Skip buckets we can't access
  205. logger.debug(f"Failed to get notifications for bucket {bucket_name}: {str(e)}")
  206. except Exception as e:
  207. logger.warning(f"Failed to scan S3 event notifications: {str(e)}")
  208. return resources